lua-cli 3.16.2 → 3.17.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.
package/dist/index.js CHANGED
@@ -132,13 +132,14 @@ var init_semver = __esm({
132
132
  // src/config/constants.ts
133
133
  import { join } from "path";
134
134
  import { homedir } from "os";
135
- var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
135
+ var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
136
136
  var init_constants = __esm({
137
137
  "src/config/constants.ts"() {
138
138
  "use strict";
139
139
  CLI_CONFIG_DIR = join(homedir(), ".lua-cli");
140
140
  VERSION_CHECK_FILE = join(CLI_CONFIG_DIR, "version-check.json");
141
141
  TELEMETRY_FILE = join(CLI_CONFIG_DIR, "telemetry.json");
142
+ CLI_CACHE_FILE = join(CLI_CONFIG_DIR, "cache.json");
142
143
  BASE_URLS = {
143
144
  API: process.env.LUA_API_URL || "https://api.heylua.ai",
144
145
  AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
@@ -153,6 +154,7 @@ var init_constants = __esm({
153
154
  "base"
154
155
  ];
155
156
  SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
157
+ AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
156
158
  POSTHOG_API_KEY = "phc_W7Qsquwlflshmdkm2hWSqRpXuxGbVFo7LEX8H9HrSjC";
157
159
  POSTHOG_HOST = "https://us.i.posthog.com";
158
160
  }
@@ -1198,7 +1200,14 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
1198
1200
  // `Data.get('call:<sessionId>')` to drive post-call analytics, follow-ups,
1199
1201
  // or QA workflows. Defaults to false — most calls don't need to keep
1200
1202
  // a transcript copy.
1201
- persistTranscript: z.boolean().optional()
1203
+ persistTranscript: z.boolean().optional(),
1204
+ // Spoken acknowledgement played when a tool call fails. When a tool
1205
+ // throws, times out, or returns an unsupported result, the adapter calls
1206
+ // `session.say(text)` once per failed call before surfacing the error
1207
+ // to the LLM as a ToolError — fills the 2–3s gap before the LLM's own
1208
+ // recovery response. Persona-specific (keep it short and on-brand);
1209
+ // absent → no spoken fallback (the LLM's recovery is the only signal).
1210
+ onToolFailureSay: z.string().min(1).max(200).optional()
1202
1211
  });
1203
1212
  LuaVoiceConfigSchema = LuaVoiceConfigInnerSchema.superRefine((cfg, ctx) => {
1204
1213
  const isRealtime = cfg.llm.kind === "realtime";
@@ -1526,7 +1535,7 @@ var init_artifact_loader = __esm({
1526
1535
  });
1527
1536
 
1528
1537
  // src/api/backup.api.service.ts
1529
- var BackupApi;
1538
+ var BackupApi, backup_api_service_default;
1530
1539
  var init_backup_api_service = __esm({
1531
1540
  "src/api/backup.api.service.ts"() {
1532
1541
  "use strict";
@@ -1645,7 +1654,20 @@ var init_backup_api_service = __esm({
1645
1654
  Authorization: `Bearer ${this.apiKey}`
1646
1655
  });
1647
1656
  }
1657
+ /**
1658
+ * Get the list of backup versions for this agent.
1659
+ *
1660
+ * @param all - If true, lifts the server-side 50-version cap
1661
+ * @returns Promise resolving to the list of version summaries (newest first)
1662
+ */
1663
+ async getBackupVersions(all = false) {
1664
+ const qs = all ? "?all=true" : "";
1665
+ return this.httpGet(`/developer/agents/${this.agentId}/backup/versions${qs}`, {
1666
+ Authorization: `Bearer ${this.apiKey}`
1667
+ });
1668
+ }
1648
1669
  };
1670
+ backup_api_service_default = BackupApi;
1649
1671
  }
1650
1672
  });
1651
1673
 
@@ -1662,7 +1684,7 @@ async function ensureBundlesUploaded(apiKey, agentId, bundles) {
1662
1684
  uploaded: 0
1663
1685
  };
1664
1686
  }
1665
- const api = new BackupApi(BASE_URLS.API, apiKey, agentId);
1687
+ const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
1666
1688
  const hashes = Array.from(bundles.keys());
1667
1689
  const urlResponse = await api.getBlobUploadUrls(hashes);
1668
1690
  if (!urlResponse.success) {
@@ -5053,11 +5075,11 @@ var init_mcp_server_plugin = __esm({
5053
5075
  // ../shared-source-sync/dist/index.mjs
5054
5076
  import { createHash } from "crypto";
5055
5077
  import { extname } from "path";
5056
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
5078
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
5057
5079
  import { join as join4, sep } from "path";
5058
5080
  import { gunzipSync, gzipSync } from "zlib";
5059
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync22, statSync as statSync22, writeFileSync as writeFileSync5 } from "fs";
5060
- import { dirname as dirname3, join as join22, resolve, sep as sep2 } from "path";
5081
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync22, statSync as statSync22, writeFileSync as writeFileSync6 } from "fs";
5082
+ import { dirname as dirname4, join as join22, resolve, sep as sep2 } from "path";
5061
5083
  import { mkdirSync as mkdirSync22, readdirSync as readdirSync22, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync22 } from "fs";
5062
5084
  import { dirname as dirname22, join as join32, sep as sep3 } from "path";
5063
5085
  import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
@@ -5117,7 +5139,7 @@ function walkWorkspace(rootDir, opts = {}) {
5117
5139
  if (stats.size > maxBytes) continue;
5118
5140
  let content;
5119
5141
  try {
5120
- content = readFileSync6(abs);
5142
+ content = readFileSync7(abs);
5121
5143
  } catch {
5122
5144
  continue;
5123
5145
  }
@@ -5234,10 +5256,10 @@ function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
5234
5256
  continue;
5235
5257
  }
5236
5258
  }
5237
- mkdirSync5(dirname3(targetPath), {
5259
+ mkdirSync6(dirname4(targetPath), {
5238
5260
  recursive: true
5239
5261
  });
5240
- writeFileSync5(targetPath, content);
5262
+ writeFileSync6(targetPath, content);
5241
5263
  filesWritten++;
5242
5264
  }
5243
5265
  return {
@@ -5432,12 +5454,12 @@ var init_dist2 = __esm({
5432
5454
  this.options = options;
5433
5455
  this.fetchFn = options.fetch ?? fetch;
5434
5456
  }
5435
- url(path18) {
5457
+ url(path19) {
5436
5458
  const base = this.options.baseUrl.replace(/\/$/, "");
5437
- return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path18}`;
5459
+ return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path19}`;
5438
5460
  }
5439
- async json(method, path18, body) {
5440
- const endpoint = this.url(path18);
5461
+ async json(method, path19, body) {
5462
+ const endpoint = this.url(path19);
5441
5463
  const res = await this.fetchFn(endpoint, {
5442
5464
  method,
5443
5465
  headers: {
@@ -5456,7 +5478,7 @@ var init_dist2 = __esm({
5456
5478
  } catch {
5457
5479
  text2 = void 0;
5458
5480
  }
5459
- throw new BackupHttpError(`Backup request failed: ${method} ${path18} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
5481
+ throw new BackupHttpError(`Backup request failed: ${method} ${path19} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
5460
5482
  }
5461
5483
  const text = await res.text();
5462
5484
  if (!text) return void 0;
@@ -5523,16 +5545,16 @@ var init_dist2 = __esm({
5523
5545
  });
5524
5546
 
5525
5547
  // src/compiler/utils/common.ts
5526
- import path3 from "path";
5548
+ import path4 from "path";
5527
5549
  function hashContent(content) {
5528
5550
  return hashContentTruncated(content);
5529
5551
  }
5530
5552
  function isInside(child, parent) {
5531
- const resolvedChild = path3.resolve(child);
5532
- const resolvedParent = path3.resolve(parent);
5553
+ const resolvedChild = path4.resolve(child);
5554
+ const resolvedParent = path4.resolve(parent);
5533
5555
  if (resolvedChild === resolvedParent) return true;
5534
- const rel = path3.relative(resolvedParent, resolvedChild);
5535
- return !!rel && !rel.startsWith("..") && !path3.isAbsolute(rel);
5556
+ const rel = path4.relative(resolvedParent, resolvedChild);
5557
+ return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
5536
5558
  }
5537
5559
  function classifyProjectFile(name) {
5538
5560
  if (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js")) {
@@ -5561,20 +5583,20 @@ var init_common = __esm({
5561
5583
  });
5562
5584
 
5563
5585
  // src/compiler/utils/path-resolver.ts
5564
- import * as fs3 from "fs";
5565
- import * as path4 from "path";
5586
+ import * as fs4 from "fs";
5587
+ import * as path5 from "path";
5566
5588
  function getTsConfigPathMappings(rootDir) {
5567
- const resolvedRoot = path4.resolve(rootDir);
5589
+ const resolvedRoot = path5.resolve(rootDir);
5568
5590
  if (mappingsCache.has(resolvedRoot)) {
5569
5591
  return mappingsCache.get(resolvedRoot) ?? null;
5570
5592
  }
5571
- const tsconfigPath = path4.join(resolvedRoot, "tsconfig.json");
5572
- if (!fs3.existsSync(tsconfigPath)) {
5593
+ const tsconfigPath = path5.join(resolvedRoot, "tsconfig.json");
5594
+ if (!fs4.existsSync(tsconfigPath)) {
5573
5595
  mappingsCache.set(resolvedRoot, null);
5574
5596
  return null;
5575
5597
  }
5576
5598
  try {
5577
- const tsconfigContent = fs3.readFileSync(tsconfigPath, "utf-8");
5599
+ const tsconfigContent = fs4.readFileSync(tsconfigPath, "utf-8");
5578
5600
  const tsconfig = JSON.parse(tsconfigContent);
5579
5601
  const compilerOptions = tsconfig.compilerOptions || {};
5580
5602
  if (compilerOptions.paths) {
@@ -5601,7 +5623,7 @@ function resolvePathAlias(specifier, rootDir) {
5601
5623
  if (match) {
5602
5624
  const captured = match[1] || "";
5603
5625
  const targetPath = targets[0].replace("*", captured);
5604
- return path4.join(mappings.rootDir, mappings.baseUrl, targetPath);
5626
+ return path5.join(mappings.rootDir, mappings.baseUrl, targetPath);
5605
5627
  }
5606
5628
  }
5607
5629
  return null;
@@ -5613,20 +5635,20 @@ function resolveModuleSpecifier(specifier, fromFile, rootDir) {
5613
5635
  return tryExtensions(aliasResolved);
5614
5636
  }
5615
5637
  if (normalized.startsWith("./") || normalized.startsWith("../")) {
5616
- const basePath = path4.resolve(path4.dirname(fromFile), normalized);
5638
+ const basePath = path5.resolve(path5.dirname(fromFile), normalized);
5617
5639
  return tryExtensions(basePath);
5618
5640
  }
5619
5641
  return void 0;
5620
5642
  }
5621
5643
  function tryExtensions(basePath) {
5622
5644
  for (const ext of MODULE_EXTENSIONS) {
5623
- const full = ext.startsWith("/") ? path4.join(basePath, ext) : basePath + ext;
5624
- if (fs3.existsSync(full)) return full;
5645
+ const full = ext.startsWith("/") ? path5.join(basePath, ext) : basePath + ext;
5646
+ if (fs4.existsSync(full)) return full;
5625
5647
  }
5626
5648
  return void 0;
5627
5649
  }
5628
5650
  function registerProjectRootDir(project, rootDir) {
5629
- projectRootDirs.set(project, path4.resolve(rootDir));
5651
+ projectRootDirs.set(project, path5.resolve(rootDir));
5630
5652
  }
5631
5653
  function getProjectRootDir(project) {
5632
5654
  return projectRootDirs.get(project);
@@ -6226,7 +6248,7 @@ var init_reference_resolver = __esm({
6226
6248
  });
6227
6249
 
6228
6250
  // src/compiler/plugins/skill.plugin.ts
6229
- import fs4 from "fs/promises";
6251
+ import fs5 from "fs/promises";
6230
6252
  import { Node as Node11 } from "ts-morph";
6231
6253
  function findEnclosingVariableDeclaration(config) {
6232
6254
  let current = config.getParent();
@@ -6559,7 +6581,7 @@ var init_skill_plugin = __esm({
6559
6581
  description: metadata.description,
6560
6582
  metadata: this.sanitizeForPersistence(metadata.metadata)
6561
6583
  }, null, 2);
6562
- const originalSource = await fs4.readFile(metadata.sourcePath, "utf-8");
6584
+ const originalSource = await fs5.readFile(metadata.sourcePath, "utf-8");
6563
6585
  return {
6564
6586
  code,
6565
6587
  sourceMap: "",
@@ -6663,6 +6685,31 @@ var init_skill_plugin = __esm({
6663
6685
 
6664
6686
  // src/compiler/plugins/agent.plugin.ts
6665
6687
  import { Node as Node12 } from "ts-morph";
6688
+ function shapeModelSettings(raw) {
6689
+ const KNOWN_KEYS = [
6690
+ "temperature",
6691
+ "topP",
6692
+ "topK",
6693
+ "maxOutputTokens",
6694
+ "presencePenalty",
6695
+ "frequencyPenalty",
6696
+ "stopSequences",
6697
+ "seed"
6698
+ ];
6699
+ const result = {};
6700
+ for (const key of KNOWN_KEYS) {
6701
+ const value = raw[key];
6702
+ if (value === void 0) continue;
6703
+ if (key === "stopSequences") {
6704
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
6705
+ result[key] = value;
6706
+ }
6707
+ } else if (typeof value === "number" && Number.isFinite(value)) {
6708
+ result[key] = value;
6709
+ }
6710
+ }
6711
+ return Object.keys(result).length > 0 ? result : void 0;
6712
+ }
6666
6713
  var AgentPlugin, agentPlugin;
6667
6714
  var init_agent_plugin = __esm({
6668
6715
  "src/compiler/plugins/agent.plugin.ts"() {
@@ -6695,7 +6742,8 @@ var init_agent_plugin = __esm({
6695
6742
  "name",
6696
6743
  "description",
6697
6744
  "persona",
6698
- "model"
6745
+ "model",
6746
+ "modelSettings"
6699
6747
  ]
6700
6748
  };
6701
6749
  supportsClassDefinition = true;
@@ -6752,6 +6800,9 @@ var init_agent_plugin = __esm({
6752
6800
  const governanceHit = findClassMember(classDecl, "governance", "property");
6753
6801
  const governanceObj = governanceHit && Node12.isPropertyDeclaration(governanceHit.node) ? evaluateNodeAsObject(governanceHit.node.getInitializer()) : void 0;
6754
6802
  const governance = governanceObj && typeof governanceObj.mode === "string" ? governanceObj : void 0;
6803
+ const modelSettingsHit = findClassMember(classDecl, "modelSettings", "property");
6804
+ const modelSettingsObj = modelSettingsHit && Node12.isPropertyDeclaration(modelSettingsHit.node) ? evaluateNodeAsObject(modelSettingsHit.node.getInitializer()) : void 0;
6805
+ const modelSettings = modelSettingsObj ? shapeModelSettings(modelSettingsObj) : void 0;
6755
6806
  return {
6756
6807
  kind: this.kind,
6757
6808
  name,
@@ -6766,6 +6817,7 @@ var init_agent_plugin = __esm({
6766
6817
  persona,
6767
6818
  model,
6768
6819
  hasModelResolver,
6820
+ modelSettings,
6769
6821
  batching,
6770
6822
  governance
6771
6823
  }
@@ -6795,6 +6847,7 @@ var init_agent_plugin = __esm({
6795
6847
  "text"
6796
6848
  ]) ?? "";
6797
6849
  const { model, hasModelResolver } = this.extractModelInfo(config);
6850
+ const modelSettings = this.extractModelSettings(config);
6798
6851
  const batching = this.extractBatchingInfo(config);
6799
6852
  const governance = this.extractGovernanceInfo(config);
6800
6853
  const { voiceRefNames, voiceRefSourcePaths } = this.extractVoiceRefs(config);
@@ -6811,6 +6864,7 @@ var init_agent_plugin = __esm({
6811
6864
  persona,
6812
6865
  model,
6813
6866
  hasModelResolver,
6867
+ modelSettings,
6814
6868
  batching,
6815
6869
  governance,
6816
6870
  voiceRefNames,
@@ -6881,6 +6935,17 @@ var init_agent_plugin = __esm({
6881
6935
  return governance;
6882
6936
  }
6883
6937
  /**
6938
+ * Extract `modelSettings` from the config-literal path. Delegates to the
6939
+ * shared `shapeModelSettings` helper so the class-definition extraction
6940
+ * path applies the exact same key whitelist + type narrowing — without
6941
+ * the shared helper, the two paths would drift (see Bugbot #ref1).
6942
+ */
6943
+ extractModelSettings(config) {
6944
+ const raw = extractObjectProperty(config, "modelSettings");
6945
+ if (!raw || typeof raw !== "object") return void 0;
6946
+ return shapeModelSettings(raw);
6947
+ }
6948
+ /**
6884
6949
  * Extract model info from agent config.
6885
6950
  * Returns the static model string (if any) and whether a resolver function exists.
6886
6951
  */
@@ -6962,6 +7027,7 @@ var init_agent_plugin = __esm({
6962
7027
  persona: agentMeta.persona,
6963
7028
  model: agentMeta.model,
6964
7029
  hasModelResolver: agentMeta.hasModelResolver || void 0,
7030
+ modelSettings: agentMeta.modelSettings,
6965
7031
  batching: agentMeta.batching,
6966
7032
  governance: agentMeta.governance,
6967
7033
  voiceRefs
@@ -7023,6 +7089,7 @@ var init_agent_plugin = __esm({
7023
7089
  return rest;
7024
7090
  }
7025
7091
  };
7092
+ __name(shapeModelSettings, "shapeModelSettings");
7026
7093
  agentPlugin = new AgentPlugin();
7027
7094
  }
7028
7095
  });
@@ -7313,10 +7380,10 @@ var init_device_trigger_plugin = __esm({
7313
7380
  });
7314
7381
 
7315
7382
  // src/compiler/utils/primitive-rewrite.ts
7316
- import { readFileSync as readFileSync8 } from "fs";
7383
+ import { readFileSync as readFileSync9 } from "fs";
7317
7384
  import { Node as Node14, Project, ts as ts3 } from "ts-morph";
7318
7385
  function rewritePrimitiveSource(metadata, opts) {
7319
- const sourceCode = readFileSync8(metadata.sourcePath, "utf-8");
7386
+ const sourceCode = readFileSync9(metadata.sourcePath, "utf-8");
7320
7387
  const localProject = new Project({
7321
7388
  useInMemoryFileSystem: true,
7322
7389
  compilerOptions: {
@@ -7655,7 +7722,7 @@ var init_cross_file_specs = __esm({
7655
7722
  });
7656
7723
 
7657
7724
  // src/compiler/plugins/voice.plugin.ts
7658
- import fs5 from "fs/promises";
7725
+ import fs6 from "fs/promises";
7659
7726
  import { Node as Node15 } from "ts-morph";
7660
7727
  function astObjectToPlain(obj) {
7661
7728
  const result = {};
@@ -8086,6 +8153,7 @@ var init_voice_plugin = __esm({
8086
8153
  const tts = extractModelField(config, "tts");
8087
8154
  const krispEnabled = extractBooleanProperty(config, "krispEnabled");
8088
8155
  const persistTranscript = extractBooleanProperty(config, "persistTranscript");
8156
+ const onToolFailureSay = extractStringProperty(config, "onToolFailureSay");
8089
8157
  const volume = extractNumberProperty(config, "volume");
8090
8158
  const interruption = extractObjectProperty(config, "interruption");
8091
8159
  const pronunciationsRaw = extractObjectProperty(config, "pronunciations");
@@ -8122,6 +8190,7 @@ var init_voice_plugin = __esm({
8122
8190
  hasTools: hasArrayProperty(config, "tools"),
8123
8191
  krispEnabled,
8124
8192
  persistTranscript,
8193
+ onToolFailureSay,
8125
8194
  volume,
8126
8195
  pronunciations,
8127
8196
  backgroundAudio,
@@ -8181,6 +8250,7 @@ var init_voice_plugin = __esm({
8181
8250
  if (fields.sttLanguage !== void 0) candidate.sttLanguage = fields.sttLanguage;
8182
8251
  if (fields.krispEnabled !== void 0) candidate.krispEnabled = fields.krispEnabled;
8183
8252
  if (fields.persistTranscript !== void 0) candidate.persistTranscript = fields.persistTranscript;
8253
+ if (fields.onToolFailureSay !== void 0) candidate.onToolFailureSay = fields.onToolFailureSay;
8184
8254
  if (fields.volume !== void 0) candidate.volume = fields.volume;
8185
8255
  if (fields.pronunciations !== void 0) candidate.pronunciations = fields.pronunciations;
8186
8256
  if (fields.backgroundAudio !== void 0) candidate.backgroundAudio = fields.backgroundAudio;
@@ -8196,8 +8266,8 @@ var init_voice_plugin = __esm({
8196
8266
  }, "isMissingTopLevelRequired");
8197
8267
  for (const issue of parsed.error.issues) {
8198
8268
  if (isMissingTopLevelRequired(issue)) continue;
8199
- const path18 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
8200
- errors.push(validationError(`Voice config invalid at \`${path18}\`: ${issue.message}`, {
8269
+ const path19 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
8270
+ errors.push(validationError(`Voice config invalid at \`${path19}\`: ${issue.message}`, {
8201
8271
  line
8202
8272
  }));
8203
8273
  }
@@ -8278,7 +8348,7 @@ var init_voice_plugin = __esm({
8278
8348
  description: metadata.description,
8279
8349
  metadata: sanitized
8280
8350
  }, null, 2);
8281
- const originalSource = await fs5.readFile(metadata.sourcePath, "utf-8");
8351
+ const originalSource = await fs6.readFile(metadata.sourcePath, "utf-8");
8282
8352
  return {
8283
8353
  code,
8284
8354
  sourceMap: "",
@@ -8335,6 +8405,7 @@ var init_voice_plugin = __esm({
8335
8405
  volume: fields.volume,
8336
8406
  pronunciations: fields.pronunciations,
8337
8407
  persistTranscript: fields.persistTranscript,
8408
+ onToolFailureSay: fields.onToolFailureSay,
8338
8409
  interruption: fields.interruption
8339
8410
  };
8340
8411
  return entry;
@@ -8431,8 +8502,8 @@ var init_registry = __esm({
8431
8502
  });
8432
8503
 
8433
8504
  // src/compiler/bundler.ts
8434
- import fs6 from "fs/promises";
8435
- import path5 from "path";
8505
+ import fs7 from "fs/promises";
8506
+ import path6 from "path";
8436
8507
  import crypto4 from "crypto";
8437
8508
  import { build } from "esbuild";
8438
8509
  var Bundler;
@@ -8501,9 +8572,9 @@ var init_bundler = __esm({
8501
8572
  * Bundle a primitive's entry point using esbuild.
8502
8573
  */
8503
8574
  async bundle(metadata, entryPointCode) {
8504
- const tempDir = path5.join(this.options.outDir, ".temp");
8505
- const outfile = path5.join(tempDir, `${metadata.kind}-${metadata.name}.bundle.js`);
8506
- const sourceDir = path5.dirname(metadata.sourcePath);
8575
+ const tempDir = path6.join(this.options.outDir, ".temp");
8576
+ const outfile = path6.join(tempDir, `${metadata.kind}-${metadata.name}.bundle.js`);
8577
+ const sourceDir = path6.dirname(metadata.sourcePath);
8507
8578
  if (this.options.debug) {
8508
8579
  this.verbose(` Source: ${metadata.sourcePath}`);
8509
8580
  this.verbose(` Output: ${outfile}`);
@@ -8533,15 +8604,15 @@ var init_bundler = __esm({
8533
8604
  ],
8534
8605
  logLevel: this.options.debug ? "debug" : "silent"
8535
8606
  });
8536
- const code = await fs6.readFile(outfile, "utf-8");
8607
+ const code = await fs7.readFile(outfile, "utf-8");
8537
8608
  let sourceMap = "";
8538
8609
  try {
8539
- sourceMap = await fs6.readFile(outfile + ".map", "utf-8");
8610
+ sourceMap = await fs7.readFile(outfile + ".map", "utf-8");
8540
8611
  } catch {
8541
8612
  }
8542
- const originalSource = await fs6.readFile(metadata.sourcePath, "utf-8");
8613
+ const originalSource = await fs7.readFile(metadata.sourcePath, "utf-8");
8543
8614
  const hash = crypto4.createHash("sha256").update(code).digest("hex").slice(0, 16);
8544
- const stats = await fs6.stat(outfile);
8615
+ const stats = await fs7.stat(outfile);
8545
8616
  return {
8546
8617
  code,
8547
8618
  sourceMap,
@@ -8558,14 +8629,14 @@ var init_bundler = __esm({
8558
8629
  const bundler = this;
8559
8630
  const sourcePath = metadata.sourcePath;
8560
8631
  const exportName = metadata.exportName;
8561
- const sourceDir = path5.dirname(sourcePath);
8632
+ const sourceDir = path6.dirname(sourcePath);
8562
8633
  return {
8563
8634
  name: "lua-virtual-source",
8564
8635
  setup(build2) {
8565
8636
  build2.onResolve({
8566
8637
  filter: /.*/
8567
8638
  }, (args2) => {
8568
- const resolved = args2.path.startsWith(".") ? path5.resolve(args2.resolveDir, args2.path) : args2.path;
8639
+ const resolved = args2.path.startsWith(".") ? path6.resolve(args2.resolveDir, args2.path) : args2.path;
8569
8640
  const normalizedResolved = resolved.replace(/\.(ts|tsx|js|jsx)$/, "");
8570
8641
  const normalizedSource = sourcePath.replace(/\.(ts|tsx|js|jsx)$/, "");
8571
8642
  if (normalizedResolved === normalizedSource) {
@@ -8581,8 +8652,8 @@ var init_bundler = __esm({
8581
8652
  namespace: "lua-virtual-source"
8582
8653
  }, async () => {
8583
8654
  const sourceFile = bundler.project.getSourceFile(sourcePath);
8584
- let content = sourceFile?.getText() ?? await fs6.readFile(sourcePath, "utf-8");
8585
- content = bundler.stripLuaCliImportsAST(content, path5.basename(sourcePath));
8655
+ let content = sourceFile?.getText() ?? await fs7.readFile(sourcePath, "utf-8");
8656
+ content = bundler.stripLuaCliImportsAST(content, path6.basename(sourcePath));
8586
8657
  const virtualContent = metadata.isDefaultExport ? content : `${content}
8587
8658
  export { ${exportName} as __lua_target__ };
8588
8659
  `;
@@ -8610,17 +8681,17 @@ export { ${exportName} as __lua_target__ };
8610
8681
  namespace: "file"
8611
8682
  }, async (args2) => {
8612
8683
  if (args2.path.includes("node_modules")) return null;
8613
- const contents = await fs6.readFile(args2.path, "utf-8");
8684
+ const contents = await fs7.readFile(args2.path, "utf-8");
8614
8685
  if (!contents.includes("lua-cli") && !contents.includes("api-exports")) {
8615
8686
  return null;
8616
8687
  }
8617
8688
  try {
8618
- const modifiedContents = stripImports(contents, path5.basename(args2.path));
8689
+ const modifiedContents = stripImports(contents, path6.basename(args2.path));
8619
8690
  if (modifiedContents !== contents) {
8620
8691
  return {
8621
8692
  contents: modifiedContents,
8622
8693
  loader: args2.path.endsWith(".ts") || args2.path.endsWith(".tsx") ? "ts" : "js",
8623
- resolveDir: path5.dirname(args2.path)
8694
+ resolveDir: path6.dirname(args2.path)
8624
8695
  };
8625
8696
  }
8626
8697
  return null;
@@ -8657,8 +8728,8 @@ export { ${exportName} as __lua_target__ };
8657
8728
 
8658
8729
  // src/compiler/agent-traverser.ts
8659
8730
  import { Project as Project2, Node as Node16 } from "ts-morph";
8660
- import path6 from "path";
8661
- import fs7 from "fs";
8731
+ import path7 from "path";
8732
+ import fs8 from "fs";
8662
8733
  var PRIMITIVE_TYPES, AgentTraverser;
8663
8734
  var init_agent_traverser = __esm({
8664
8735
  "src/compiler/agent-traverser.ts"() {
@@ -8731,7 +8802,7 @@ var init_agent_traverser = __esm({
8731
8802
  this.rootDir = rootDir;
8732
8803
  this.debug = debug;
8733
8804
  this.project = new Project2({
8734
- tsConfigFilePath: path6.join(rootDir, "tsconfig.json"),
8805
+ tsConfigFilePath: path7.join(rootDir, "tsconfig.json"),
8735
8806
  skipAddingFilesFromTsConfig: true
8736
8807
  });
8737
8808
  registerProjectRootDir(this.project, this.rootDir);
@@ -8815,15 +8886,15 @@ var init_agent_traverser = __esm({
8815
8886
  */
8816
8887
  detectAgent() {
8817
8888
  const priorityFiles = [
8818
- path6.join(this.rootDir, "index.ts"),
8819
- path6.join(this.rootDir, "src", "index.ts"),
8820
- path6.join(this.rootDir, "agent.ts"),
8821
- path6.join(this.rootDir, "src", "agent.ts"),
8822
- path6.join(this.rootDir, "main.ts"),
8823
- path6.join(this.rootDir, "src", "main.ts")
8889
+ path7.join(this.rootDir, "index.ts"),
8890
+ path7.join(this.rootDir, "src", "index.ts"),
8891
+ path7.join(this.rootDir, "agent.ts"),
8892
+ path7.join(this.rootDir, "src", "agent.ts"),
8893
+ path7.join(this.rootDir, "main.ts"),
8894
+ path7.join(this.rootDir, "src", "main.ts")
8824
8895
  ];
8825
8896
  for (const filePath of priorityFiles) {
8826
- if (fs7.existsSync(filePath)) {
8897
+ if (fs8.existsSync(filePath)) {
8827
8898
  try {
8828
8899
  const sourceFile = this.project.addSourceFileAtPath(filePath);
8829
8900
  const agent = this.detectAgentInFile(sourceFile);
@@ -8975,7 +9046,7 @@ var init_agent_traverser = __esm({
8975
9046
  console.warn(formatSarifWarning({
8976
9047
  ruleId: "lua/missing-primitive-declaration",
8977
9048
  filePath: sourcePath,
8978
- message: `agent references ${kind} "${refName}" but ${path6.basename(sourcePath)} does not define a matching primitive`,
9049
+ message: `agent references ${kind} "${refName}" but ${path7.basename(sourcePath)} does not define a matching primitive`,
8979
9050
  hint: `export a named ${plugin.displayName.toLowerCase()} declaration as "${refName}", or adjust the import so it points to the file that does`
8980
9051
  }));
8981
9052
  return null;
@@ -8995,20 +9066,20 @@ var init_agent_traverser = __esm({
8995
9066
  });
8996
9067
 
8997
9068
  // src/compiler/utils/workspace.ts
8998
- import fs8 from "fs";
8999
- import path7 from "path";
9069
+ import fs9 from "fs";
9070
+ import path8 from "path";
9000
9071
  function findWorkspaceRoot(rootDir) {
9001
- const resolvedInput = path7.resolve(rootDir);
9072
+ const resolvedInput = path8.resolve(rootDir);
9002
9073
  const cached = workspaceRootCache.get(resolvedInput);
9003
9074
  if (cached !== void 0) return cached;
9004
9075
  let current = resolvedInput;
9005
- const { root } = path7.parse(current);
9076
+ const { root } = path8.parse(current);
9006
9077
  while (true) {
9007
9078
  if (isWorkspaceRoot(current)) {
9008
9079
  workspaceRootCache.set(resolvedInput, current);
9009
9080
  return current;
9010
9081
  }
9011
- const parent = path7.dirname(current);
9082
+ const parent = path8.dirname(current);
9012
9083
  if (parent === current || current === root) break;
9013
9084
  current = parent;
9014
9085
  }
@@ -9016,22 +9087,22 @@ function findWorkspaceRoot(rootDir) {
9016
9087
  return resolvedInput;
9017
9088
  }
9018
9089
  function isWorkspaceRoot(dir) {
9019
- if (fileExists(path7.join(dir, "pnpm-workspace.yaml"))) return true;
9020
- if (fileExists(path7.join(dir, "lerna.json"))) return true;
9021
- if (packageJsonHasWorkspaces(path7.join(dir, "package.json"))) return true;
9022
- if (dirExists(path7.join(dir, ".git"))) return true;
9090
+ if (fileExists(path8.join(dir, "pnpm-workspace.yaml"))) return true;
9091
+ if (fileExists(path8.join(dir, "lerna.json"))) return true;
9092
+ if (packageJsonHasWorkspaces(path8.join(dir, "package.json"))) return true;
9093
+ if (dirExists(path8.join(dir, ".git"))) return true;
9023
9094
  return false;
9024
9095
  }
9025
9096
  function fileExists(p) {
9026
9097
  try {
9027
- return fs8.statSync(p).isFile();
9098
+ return fs9.statSync(p).isFile();
9028
9099
  } catch {
9029
9100
  return false;
9030
9101
  }
9031
9102
  }
9032
9103
  function dirExists(p) {
9033
9104
  try {
9034
- return fs8.statSync(p).isDirectory();
9105
+ return fs9.statSync(p).isDirectory();
9035
9106
  } catch {
9036
9107
  return false;
9037
9108
  }
@@ -9039,7 +9110,7 @@ function dirExists(p) {
9039
9110
  function packageJsonHasWorkspaces(pkgPath) {
9040
9111
  if (!fileExists(pkgPath)) return false;
9041
9112
  try {
9042
- const raw = fs8.readFileSync(pkgPath, "utf-8");
9113
+ const raw = fs9.readFileSync(pkgPath, "utf-8");
9043
9114
  const json = JSON.parse(raw);
9044
9115
  if (!json.workspaces) return false;
9045
9116
  return Array.isArray(json.workspaces) || typeof json.workspaces === "object";
@@ -9061,8 +9132,8 @@ var init_workspace = __esm({
9061
9132
  });
9062
9133
 
9063
9134
  // src/compiler/compiler.ts
9064
- import fs9 from "fs/promises";
9065
- import path8 from "path";
9135
+ import fs10 from "fs/promises";
9136
+ import path9 from "path";
9066
9137
  import { Project as Project3 } from "ts-morph";
9067
9138
  async function compile(options) {
9068
9139
  const compiler = new Compiler(options);
@@ -9115,7 +9186,7 @@ var init_compiler = __esm({
9115
9186
  ...options
9116
9187
  };
9117
9188
  this.project = new Project3({
9118
- tsConfigFilePath: path8.join(options.rootDir, "tsconfig.json")
9189
+ tsConfigFilePath: path9.join(options.rootDir, "tsconfig.json")
9119
9190
  });
9120
9191
  registerProjectRootDir(this.project, this.options.rootDir);
9121
9192
  this.bundler = new Bundler({
@@ -9192,10 +9263,10 @@ var init_compiler = __esm({
9192
9263
  return await this.createResult(false, [], errors, warnings, startTime);
9193
9264
  }
9194
9265
  this.verbose("\u{1F4E6} Bundling primitives...");
9195
- await fs9.mkdir(this.options.outDir, {
9266
+ await fs10.mkdir(this.options.outDir, {
9196
9267
  recursive: true
9197
9268
  });
9198
- await fs9.mkdir(path8.join(this.options.outDir, ".temp"), {
9269
+ await fs10.mkdir(path9.join(this.options.outDir, ".temp"), {
9199
9270
  recursive: true
9200
9271
  });
9201
9272
  const CONCURRENCY = 4;
@@ -9215,7 +9286,7 @@ var init_compiler = __esm({
9215
9286
  this.verbose("\u{1F4BE} Writing artifacts...");
9216
9287
  await this.writeArtifacts(compiledPrimitives);
9217
9288
  if (!this.options.debug) {
9218
- await fs9.rm(path8.join(this.options.outDir, ".temp"), {
9289
+ await fs10.rm(path9.join(this.options.outDir, ".temp"), {
9219
9290
  recursive: true,
9220
9291
  force: true
9221
9292
  });
@@ -9258,7 +9329,7 @@ var init_compiler = __esm({
9258
9329
  */
9259
9330
  getWorkspaceRoot() {
9260
9331
  if (this._workspaceRoot === void 0) {
9261
- this._workspaceRoot = findWorkspaceRoot(path8.resolve(this.options.rootDir));
9332
+ this._workspaceRoot = findWorkspaceRoot(path9.resolve(this.options.rootDir));
9262
9333
  }
9263
9334
  return this._workspaceRoot;
9264
9335
  }
@@ -9272,18 +9343,18 @@ var init_compiler = __esm({
9272
9343
  * - Files outside the detected workspace root (not our code)
9273
9344
  */
9274
9345
  collectExternalWorkspaceFiles(traverser) {
9275
- const rootDir = path8.resolve(this.options.rootDir);
9346
+ const rootDir = path9.resolve(this.options.rootDir);
9276
9347
  const workspaceRoot = this.getWorkspaceRoot();
9277
9348
  if (workspaceRoot === rootDir) return [];
9278
9349
  const loaded = traverser.getAllLoadedSourceFilePaths();
9279
9350
  const result = [];
9280
9351
  const seen = /* @__PURE__ */ new Set();
9281
9352
  for (const raw of loaded) {
9282
- const abs = path8.resolve(raw);
9353
+ const abs = path9.resolve(raw);
9283
9354
  if (seen.has(abs)) continue;
9284
9355
  seen.add(abs);
9285
9356
  if (isInside(abs, rootDir)) continue;
9286
- if (abs.includes(`${path8.sep}node_modules${path8.sep}`)) continue;
9357
+ if (abs.includes(`${path9.sep}node_modules${path9.sep}`)) continue;
9287
9358
  if (!isInside(abs, workspaceRoot)) continue;
9288
9359
  result.push(abs);
9289
9360
  }
@@ -9416,9 +9487,9 @@ var init_compiler = __esm({
9416
9487
  * @param primitives - All compiled primitives to write
9417
9488
  */
9418
9489
  async writeArtifacts(primitives) {
9419
- const artifactsDir = path8.join(this.options.outDir, "artifacts");
9420
- const sourcesDir = path8.join(this.options.outDir, "sources");
9421
- await fs9.mkdir(sourcesDir, {
9490
+ const artifactsDir = path9.join(this.options.outDir, "artifacts");
9491
+ const sourcesDir = path9.join(this.options.outDir, "sources");
9492
+ await fs10.mkdir(sourcesDir, {
9422
9493
  recursive: true
9423
9494
  });
9424
9495
  const writtenSources = /* @__PURE__ */ new Map();
@@ -9426,7 +9497,7 @@ var init_compiler = __esm({
9426
9497
  this.verbose(" \u{1F4C2} Collecting project files...");
9427
9498
  const projectFiles = await this.storeProjectFiles(sourcesDir, writtenSources);
9428
9499
  const manifest = await this.createManifest(primitives, projectFiles);
9429
- await fs9.writeFile(path8.join(this.options.outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
9500
+ await fs10.writeFile(path9.join(this.options.outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
9430
9501
  this.verbose(` \u{1F4E6} Total files stored: ${writtenSources.size} (deduplicated)`);
9431
9502
  this.verbose(` \u{1F4C1} Project files: ${projectFiles.length}`);
9432
9503
  if (this.options.verbose || this.options.debug) {
@@ -9438,25 +9509,25 @@ var init_compiler = __esm({
9438
9509
  */
9439
9510
  async writePrimitiveArtifacts(primitives, artifactsDir, sourcesDir, writtenSources) {
9440
9511
  for (const primitive of primitives) {
9441
- const primitiveDir = path8.join(artifactsDir, primitive.kind);
9442
- await fs9.mkdir(primitiveDir, {
9512
+ const primitiveDir = path9.join(artifactsDir, primitive.kind);
9513
+ await fs10.mkdir(primitiveDir, {
9443
9514
  recursive: true
9444
9515
  });
9445
9516
  const baseName = primitive.name;
9446
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.js`), primitive.artifact.code);
9517
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js`), primitive.artifact.code);
9447
9518
  if (primitive.artifact.sourceMap) {
9448
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.js.map`), primitive.artifact.sourceMap);
9519
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js.map`), primitive.artifact.sourceMap);
9449
9520
  }
9450
9521
  const sourceHash = hashContent(primitive.artifact.originalSource);
9451
9522
  if (!writtenSources.has(sourceHash)) {
9452
9523
  const ext = primitive.sourcePath.endsWith(".tsx") ? ".tsx" : ".ts";
9453
- await fs9.writeFile(path8.join(sourcesDir, `${sourceHash}${ext}`), primitive.artifact.originalSource);
9454
- const relativePath = path8.relative(this.options.rootDir, primitive.sourcePath);
9524
+ await fs10.writeFile(path9.join(sourcesDir, `${sourceHash}${ext}`), primitive.artifact.originalSource);
9525
+ const relativePath = path9.relative(this.options.rootDir, primitive.sourcePath);
9455
9526
  writtenSources.set(sourceHash, relativePath);
9456
9527
  }
9457
9528
  const sidecarPlugin = pluginRegistry.get(primitive.kind);
9458
9529
  const sidecarMetadata = sidecarPlugin?.sanitizeForPersistence ? sidecarPlugin.sanitizeForPersistence(primitive.metadata) : primitive.metadata;
9459
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.json`), JSON.stringify({
9530
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.json`), JSON.stringify({
9460
9531
  kind: primitive.kind,
9461
9532
  name: primitive.name,
9462
9533
  description: primitive.description,
@@ -9478,12 +9549,12 @@ var init_compiler = __esm({
9478
9549
  const allFiles = await this.collectProjectFiles();
9479
9550
  const workspaceRoot = this.getWorkspaceRoot();
9480
9551
  for (const file of allFiles) {
9481
- const absPath = file.external ? path8.join(workspaceRoot, file.relativePath) : path8.join(this.options.rootDir, file.relativePath);
9482
- const content = await fs9.readFile(absPath, "utf-8");
9552
+ const absPath = file.external ? path9.join(workspaceRoot, file.relativePath) : path9.join(this.options.rootDir, file.relativePath);
9553
+ const content = await fs10.readFile(absPath, "utf-8");
9483
9554
  const hash = hashContent(content);
9484
9555
  if (!writtenSources.has(hash)) {
9485
- const ext = path8.extname(file.relativePath) || ".txt";
9486
- await fs9.writeFile(path8.join(sourcesDir, `${hash}${ext}`), content);
9556
+ const ext = path9.extname(file.relativePath) || ".txt";
9557
+ await fs10.writeFile(path9.join(sourcesDir, `${hash}${ext}`), content);
9487
9558
  writtenSources.set(hash, file.relativePath);
9488
9559
  }
9489
9560
  projectFiles.push({
@@ -9559,12 +9630,12 @@ var init_compiler = __esm({
9559
9630
  async collectProjectFiles() {
9560
9631
  const files = [];
9561
9632
  const scanDir = /* @__PURE__ */ __name(async (dir, relativeBase = "") => {
9562
- const entries = await fs9.readdir(dir, {
9633
+ const entries = await fs10.readdir(dir, {
9563
9634
  withFileTypes: true
9564
9635
  });
9565
9636
  for (const entry of entries) {
9566
9637
  const relativePath = relativeBase ? `${relativeBase}/${entry.name}` : entry.name;
9567
- const fullPath = path8.join(dir, entry.name);
9638
+ const fullPath = path9.join(dir, entry.name);
9568
9639
  if (entry.isDirectory()) {
9569
9640
  if (shouldSkipDirectory(entry.name)) continue;
9570
9641
  await scanDir(fullPath, relativePath);
@@ -9581,10 +9652,10 @@ var init_compiler = __esm({
9581
9652
  const workspaceRoot = this.getWorkspaceRoot();
9582
9653
  const externalSeen = /* @__PURE__ */ new Set();
9583
9654
  for (const abs of this.externalWorkspaceFiles) {
9584
- const rel = path8.relative(workspaceRoot, abs).split(path8.sep).join("/");
9655
+ const rel = path9.relative(workspaceRoot, abs).split(path9.sep).join("/");
9585
9656
  if (!rel || rel.startsWith("..")) continue;
9586
9657
  if (externalSeen.has(rel)) continue;
9587
- const name = path8.basename(abs);
9658
+ const name = path9.basename(abs);
9588
9659
  if (shouldSkipFile(rel) || shouldSkipFile(name)) continue;
9589
9660
  externalSeen.add(rel);
9590
9661
  files.push({
@@ -9685,13 +9756,13 @@ var init_compiler = __esm({
9685
9756
  });
9686
9757
 
9687
9758
  // src/compiler/utils/file-discovery.ts
9688
- import path9 from "path";
9689
- import fs10 from "fs";
9759
+ import path10 from "path";
9760
+ import fs11 from "fs";
9690
9761
  function findEntryPoint(projectDir) {
9691
9762
  const baseDir = projectDir || process.cwd();
9692
9763
  for (const relativePath of ENTRY_POINT_PRIORITY) {
9693
- const fullPath = path9.join(baseDir, relativePath);
9694
- if (fs10.existsSync(fullPath)) {
9764
+ const fullPath = path10.join(baseDir, relativePath);
9765
+ if (fs11.existsSync(fullPath)) {
9695
9766
  return fullPath;
9696
9767
  }
9697
9768
  }
@@ -9728,8 +9799,8 @@ var init_file_discovery = __esm({
9728
9799
 
9729
9800
  // src/compiler/source-writer.ts
9730
9801
  import { Project as Project4, Node as Node17 } from "ts-morph";
9731
- import fs11 from "fs";
9732
- import path10 from "path";
9802
+ import fs12 from "fs";
9803
+ import path11 from "path";
9733
9804
  function resolveEntryPath(options) {
9734
9805
  let indexPath;
9735
9806
  try {
@@ -9741,14 +9812,14 @@ function resolveEntryPath(options) {
9741
9812
  indexPath = entryPoint;
9742
9813
  } else {
9743
9814
  const baseDir = options?.projectDir || process.cwd();
9744
- indexPath = path10.join(baseDir, "src", "index.ts");
9815
+ indexPath = path11.join(baseDir, "src", "index.ts");
9745
9816
  }
9746
9817
  }
9747
9818
  } catch {
9748
9819
  const baseDir = options?.projectDir || process.cwd();
9749
- indexPath = path10.join(baseDir, "src", "index.ts");
9820
+ indexPath = path11.join(baseDir, "src", "index.ts");
9750
9821
  }
9751
- if (!fs11.existsSync(indexPath)) {
9822
+ if (!fs12.existsSync(indexPath)) {
9752
9823
  console.warn(`Warning: Entry file not found at ${indexPath}`);
9753
9824
  return null;
9754
9825
  }
@@ -13738,6 +13809,164 @@ __name(promptAuthMethod, "promptAuthMethod");
13738
13809
 
13739
13810
  // src/commands/configure.ts
13740
13811
  init_analytics();
13812
+
13813
+ // src/utils/versioning-mode-cache.ts
13814
+ import * as fs3 from "fs";
13815
+ import * as path3 from "path";
13816
+
13817
+ // src/api/agent-version.api.service.ts
13818
+ init_http_client();
13819
+ var AgentVersionApi = class extends HttpClient {
13820
+ static {
13821
+ __name(this, "AgentVersionApi");
13822
+ }
13823
+ apiKey;
13824
+ agentId;
13825
+ constructor(baseUrl, apiKey, agentId) {
13826
+ super(baseUrl);
13827
+ this.apiKey = apiKey;
13828
+ this.agentId = agentId;
13829
+ }
13830
+ get basePath() {
13831
+ return `/developer/agents/${encodeURIComponent(this.agentId)}`;
13832
+ }
13833
+ get authHeader() {
13834
+ return {
13835
+ Authorization: `Bearer ${this.apiKey}`
13836
+ };
13837
+ }
13838
+ // ---------------------------------------------------------------------------
13839
+ // Versioning mode
13840
+ // ---------------------------------------------------------------------------
13841
+ async getVersioningMode() {
13842
+ return this.httpGet(`${this.basePath}/versioning/mode`, this.authHeader);
13843
+ }
13844
+ // ---------------------------------------------------------------------------
13845
+ // Version CRUD
13846
+ // ---------------------------------------------------------------------------
13847
+ async createVersion(body) {
13848
+ return this.httpPost(`${this.basePath}/versions`, body, this.authHeader);
13849
+ }
13850
+ async listVersions(query) {
13851
+ const params = new URLSearchParams();
13852
+ if (query?.all !== void 0) params.append("all", String(query.all));
13853
+ if (query?.limit !== void 0) params.append("limit", String(query.limit));
13854
+ if (query?.status !== void 0) params.append("status", query.status);
13855
+ const qs = params.toString();
13856
+ const url = qs ? `${this.basePath}/versions?${qs}` : `${this.basePath}/versions`;
13857
+ return this.httpGet(url, this.authHeader);
13858
+ }
13859
+ async getVersion(version) {
13860
+ return this.httpGet(`${this.basePath}/versions/${version}`, this.authHeader);
13861
+ }
13862
+ async deleteVersion(version) {
13863
+ return this.httpDelete(`${this.basePath}/versions/${version}`, this.authHeader);
13864
+ }
13865
+ // ---------------------------------------------------------------------------
13866
+ // Diff
13867
+ // ---------------------------------------------------------------------------
13868
+ async diffVersions(from, to) {
13869
+ const url = `${this.basePath}/versions/diff?from=${from}&to=${to}`;
13870
+ return this.httpGet(url, this.authHeader);
13871
+ }
13872
+ // ---------------------------------------------------------------------------
13873
+ // Promote
13874
+ // ---------------------------------------------------------------------------
13875
+ async promoteVersion(version) {
13876
+ return this.httpPost(`${this.basePath}/versions/${version}/promote`, {}, this.authHeader);
13877
+ }
13878
+ // ---------------------------------------------------------------------------
13879
+ // Patch commit hash — called by `lua version create` after a successful git
13880
+ // commit + tag to propagate the SHA to the backend AgentVersion record.
13881
+ // Failure is non-fatal; the version snapshot is valid whether or not this
13882
+ // PATCH lands.
13883
+ // ---------------------------------------------------------------------------
13884
+ async patchCommitHash(version, commitHash) {
13885
+ return this.httpPatch(`${this.basePath}/versions/${version}/commit-hash`, {
13886
+ commitHash
13887
+ }, this.authHeader);
13888
+ }
13889
+ };
13890
+
13891
+ // src/utils/versioning-mode-cache.ts
13892
+ init_constants();
13893
+ init_auth_error();
13894
+ init_constants();
13895
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
13896
+ function readCacheFile() {
13897
+ try {
13898
+ const raw = fs3.readFileSync(CLI_CACHE_FILE, "utf-8");
13899
+ return JSON.parse(raw);
13900
+ } catch {
13901
+ return {};
13902
+ }
13903
+ }
13904
+ __name(readCacheFile, "readCacheFile");
13905
+ function writeCacheFile(data) {
13906
+ try {
13907
+ fs3.mkdirSync(path3.dirname(CLI_CACHE_FILE), {
13908
+ recursive: true
13909
+ });
13910
+ fs3.writeFileSync(CLI_CACHE_FILE, JSON.stringify(data, null, 2));
13911
+ } catch {
13912
+ }
13913
+ }
13914
+ __name(writeCacheFile, "writeCacheFile");
13915
+ function isFresh(entry) {
13916
+ const age = Date.now() - new Date(entry.cachedAt).getTime();
13917
+ return age >= 0 && age < CACHE_TTL_MS;
13918
+ }
13919
+ __name(isFresh, "isFresh");
13920
+ async function getVersioningModeCached(apiKey, agentId) {
13921
+ const file = readCacheFile();
13922
+ const existing = file.versioningMode?.[agentId];
13923
+ if (existing && isFresh(existing)) {
13924
+ return {
13925
+ enabled: existing.enabled
13926
+ };
13927
+ }
13928
+ try {
13929
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
13930
+ const response = await api.getVersioningMode();
13931
+ if (!response.success || !response.data) {
13932
+ if (existing) return {
13933
+ enabled: existing.enabled
13934
+ };
13935
+ return {
13936
+ enabled: false
13937
+ };
13938
+ }
13939
+ const enabled = response.data.enabled === true;
13940
+ file.versioningMode = file.versioningMode ?? {};
13941
+ file.versioningMode[agentId] = {
13942
+ enabled,
13943
+ cachedAt: (/* @__PURE__ */ new Date()).toISOString()
13944
+ };
13945
+ writeCacheFile(file);
13946
+ return {
13947
+ enabled
13948
+ };
13949
+ } catch (err) {
13950
+ if (err instanceof AuthenticationError) throw err;
13951
+ if (err instanceof Error && /access denied|forbidden|403|401/i.test(err.message)) throw err;
13952
+ if (existing) return {
13953
+ enabled: existing.enabled
13954
+ };
13955
+ return {
13956
+ enabled: false
13957
+ };
13958
+ }
13959
+ }
13960
+ __name(getVersioningModeCached, "getVersioningModeCached");
13961
+ function invalidateVersioningModeCache() {
13962
+ const file = readCacheFile();
13963
+ if (!file.versioningMode) return;
13964
+ delete file.versioningMode;
13965
+ writeCacheFile(file);
13966
+ }
13967
+ __name(invalidateVersioningModeCache, "invalidateVersioningModeCache");
13968
+
13969
+ // src/commands/configure.ts
13741
13970
  async function configureCommand(options = {}) {
13742
13971
  return withErrorHandling(async () => {
13743
13972
  const { apiKey, email, otp } = options;
@@ -13747,11 +13976,13 @@ async function configureCommand(options = {}) {
13747
13976
  authMethod = "api-key";
13748
13977
  nonInteractive = true;
13749
13978
  await handleApiKeyAuthNonInteractive(apiKey);
13979
+ invalidateVersioningModeCache();
13750
13980
  } else if (email) {
13751
13981
  authMethod = "email";
13752
13982
  nonInteractive = true;
13753
13983
  if (otp) {
13754
13984
  await handleEmailOtpVerify(email, otp);
13985
+ invalidateVersioningModeCache();
13755
13986
  } else {
13756
13987
  await handleEmailOtpRequest(email);
13757
13988
  }
@@ -13760,8 +13991,10 @@ async function configureCommand(options = {}) {
13760
13991
  clearPromptLines(2);
13761
13992
  if (authMethod === "api-key") {
13762
13993
  await handleApiKeyAuth();
13994
+ invalidateVersioningModeCache();
13763
13995
  } else if (authMethod === "email") {
13764
13996
  await handleEmailAuth();
13997
+ invalidateVersioningModeCache();
13765
13998
  }
13766
13999
  }
13767
14000
  trackEvent("cli_auth_completed", {
@@ -14433,8 +14666,8 @@ init_compile_constants();
14433
14666
  init_artifact_loader();
14434
14667
  init_backup_api_service();
14435
14668
  init_constants();
14436
- import fs12 from "fs";
14437
- import path11 from "path";
14669
+ import fs13 from "fs";
14670
+ import path12 from "path";
14438
14671
  var BACKUP_CACHE_FILENAME = "backup-manifest.json";
14439
14672
  function calculateProjectHash(projectFiles) {
14440
14673
  return combineFileHashes(projectFiles);
@@ -14447,11 +14680,11 @@ function getCurrentProjectHash(projectPath = process.cwd()) {
14447
14680
  }
14448
14681
  __name(getCurrentProjectHash, "getCurrentProjectHash");
14449
14682
  function loadSourceByHash(hash, relativePath, projectPath = process.cwd()) {
14450
- const sourcesDir = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14451
- const ext = path11.extname(relativePath) || ".txt";
14452
- const sourcePath = path11.join(sourcesDir, `${hash}${ext}`);
14453
- if (fs12.existsSync(sourcePath)) {
14454
- return fs12.readFileSync(sourcePath, "utf-8");
14683
+ const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14684
+ const ext = path12.extname(relativePath) || ".txt";
14685
+ const sourcePath = path12.join(sourcesDir, `${hash}${ext}`);
14686
+ if (fs13.existsSync(sourcePath)) {
14687
+ return fs13.readFileSync(sourcePath, "utf-8");
14455
14688
  }
14456
14689
  return null;
14457
14690
  }
@@ -14470,30 +14703,30 @@ function prepareFileRefs(projectPath = process.cwd()) {
14470
14703
  }
14471
14704
  __name(prepareFileRefs, "prepareFileRefs");
14472
14705
  function reconcileManifestWithDisk(projectPath = process.cwd()) {
14473
- const manifestPath = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "manifest.json");
14474
- if (!fs12.existsSync(manifestPath)) return;
14706
+ const manifestPath = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "manifest.json");
14707
+ if (!fs13.existsSync(manifestPath)) return;
14475
14708
  const manifest = loadManifest(projectPath);
14476
- const sourcesDir = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14477
- fs12.mkdirSync(sourcesDir, {
14709
+ const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14710
+ fs13.mkdirSync(sourcesDir, {
14478
14711
  recursive: true
14479
14712
  });
14480
14713
  let changed = false;
14481
14714
  for (const file of manifest.projectFiles) {
14482
14715
  if (file.external) continue;
14483
- const absPath = path11.join(projectPath, file.relativePath);
14484
- if (!fs12.existsSync(absPath)) continue;
14716
+ const absPath = path12.join(projectPath, file.relativePath);
14717
+ if (!fs13.existsSync(absPath)) continue;
14485
14718
  let content;
14486
14719
  try {
14487
- content = fs12.readFileSync(absPath, "utf-8");
14720
+ content = fs13.readFileSync(absPath, "utf-8");
14488
14721
  } catch {
14489
14722
  continue;
14490
14723
  }
14491
14724
  const freshHash = hashContent(content);
14492
14725
  if (freshHash === file.hash) continue;
14493
- const ext = path11.extname(file.relativePath) || ".txt";
14494
- const sourceTargetPath = path11.join(sourcesDir, `${freshHash}${ext}`);
14726
+ const ext = path12.extname(file.relativePath) || ".txt";
14727
+ const sourceTargetPath = path12.join(sourcesDir, `${freshHash}${ext}`);
14495
14728
  try {
14496
- fs12.writeFileSync(sourceTargetPath, content, "utf-8");
14729
+ fs13.writeFileSync(sourceTargetPath, content, "utf-8");
14497
14730
  } catch {
14498
14731
  continue;
14499
14732
  }
@@ -14502,7 +14735,7 @@ function reconcileManifestWithDisk(projectPath = process.cwd()) {
14502
14735
  changed = true;
14503
14736
  }
14504
14737
  if (changed) {
14505
- fs12.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
14738
+ fs13.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
14506
14739
  }
14507
14740
  }
14508
14741
  __name(reconcileManifestWithDisk, "reconcileManifestWithDisk");
@@ -14575,7 +14808,7 @@ function checkRestoreConflicts(manifest, targetDir) {
14575
14808
  const existingFiles = [];
14576
14809
  for (const file of manifest.files) {
14577
14810
  const filePath = resolveBackupFileTarget2(file, targetDir);
14578
- if (fs12.existsSync(filePath)) {
14811
+ if (fs13.existsSync(filePath)) {
14579
14812
  existingFiles.push(file.relativePath);
14580
14813
  } else {
14581
14814
  newFiles.push(file.relativePath);
@@ -14601,16 +14834,16 @@ function createBackupTracking(projectHash) {
14601
14834
  }
14602
14835
  __name(createBackupTracking, "createBackupTracking");
14603
14836
  function getBackupCachePath(projectPath = process.cwd()) {
14604
- return path11.join(projectPath, ".lua", BACKUP_CACHE_FILENAME);
14837
+ return path12.join(projectPath, ".lua", BACKUP_CACHE_FILENAME);
14605
14838
  }
14606
14839
  __name(getBackupCachePath, "getBackupCachePath");
14607
14840
  function writeBackupManifestCache(cache, projectPath = process.cwd()) {
14608
14841
  const cachePath = getBackupCachePath(projectPath);
14609
14842
  try {
14610
- fs12.mkdirSync(path11.dirname(cachePath), {
14843
+ fs13.mkdirSync(path12.dirname(cachePath), {
14611
14844
  recursive: true
14612
14845
  });
14613
- fs12.writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
14846
+ fs13.writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
14614
14847
  } catch (error) {
14615
14848
  const message = error instanceof Error ? error.message : String(error);
14616
14849
  console.warn(`Could not write backup manifest cache: ${message}`);
@@ -14619,9 +14852,9 @@ function writeBackupManifestCache(cache, projectPath = process.cwd()) {
14619
14852
  __name(writeBackupManifestCache, "writeBackupManifestCache");
14620
14853
  function readBackupManifestCache(projectPath = process.cwd()) {
14621
14854
  const cachePath = getBackupCachePath(projectPath);
14622
- if (!fs12.existsSync(cachePath)) return null;
14855
+ if (!fs13.existsSync(cachePath)) return null;
14623
14856
  try {
14624
- const raw = fs12.readFileSync(cachePath, "utf-8");
14857
+ const raw = fs13.readFileSync(cachePath, "utf-8");
14625
14858
  const parsed = JSON.parse(raw);
14626
14859
  if (typeof parsed.lastHash !== "string" || typeof parsed.lastPushedAt !== "string" || !Array.isArray(parsed.files) || !parsed.files.every((f) => f && typeof f.relativePath === "string" && typeof f.hash === "string")) {
14627
14860
  return null;
@@ -14648,18 +14881,18 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
14648
14881
  f.hash
14649
14882
  ]));
14650
14883
  const conflicts = /* @__PURE__ */ new Set();
14651
- const projectRoot = path11.resolve(projectPath);
14884
+ const projectRoot = path12.resolve(projectPath);
14652
14885
  for (const incoming of incomingFiles) {
14653
14886
  if (COMPILE_MANAGED_FILES.has(incoming.relativePath)) continue;
14654
14887
  if (incoming.external) continue;
14655
- const absPath = path11.resolve(projectRoot, incoming.relativePath);
14656
- if (absPath !== projectRoot && !absPath.startsWith(projectRoot + path11.sep)) {
14888
+ const absPath = path12.resolve(projectRoot, incoming.relativePath);
14889
+ if (absPath !== projectRoot && !absPath.startsWith(projectRoot + path12.sep)) {
14657
14890
  continue;
14658
14891
  }
14659
- if (!fs12.existsSync(absPath)) continue;
14892
+ if (!fs13.existsSync(absPath)) continue;
14660
14893
  let diskHash;
14661
14894
  try {
14662
- const bytes = fs12.readFileSync(absPath, "utf-8");
14895
+ const bytes = fs13.readFileSync(absPath, "utf-8");
14663
14896
  diskHash = hashContent(bytes);
14664
14897
  } catch {
14665
14898
  conflicts.add(incoming.relativePath);
@@ -14686,7 +14919,7 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
14686
14919
  __name(detectLocalConflictsForPull, "detectLocalConflictsForPull");
14687
14920
  async function fetchBackupManifestForPull(apiKey, agentId) {
14688
14921
  try {
14689
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14922
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14690
14923
  const response = await backupApi.getBackupManifest();
14691
14924
  return {
14692
14925
  fetched: response.data ?? null
@@ -14717,7 +14950,7 @@ __name(decideBackupFreshness, "decideBackupFreshness");
14717
14950
  async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = process.cwd()) {
14718
14951
  const local = readBackupManifestCache(projectPath);
14719
14952
  try {
14720
- const api = new BackupApi(BASE_URLS.API, apiKey, agentId);
14953
+ const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14721
14954
  const resp = await api.getBackupMetadata();
14722
14955
  const metadata = resp.success && resp.data ? {
14723
14956
  projectHash: resp.data.projectHash
@@ -14730,7 +14963,7 @@ async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = pr
14730
14963
  __name(checkServerBackupNewerThanLocal, "checkServerBackupNewerThanLocal");
14731
14964
  async function checkBackupAvailability(apiKey, agentId) {
14732
14965
  try {
14733
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14966
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14734
14967
  const metadata = await backupApi.getBackupMetadata();
14735
14968
  if (metadata.data && metadata.data.fileCount > 0) {
14736
14969
  return {
@@ -14752,7 +14985,7 @@ __name(checkBackupAvailability, "checkBackupAvailability");
14752
14985
  async function restoreFromBackupForSync(apiKey, agentId, expectedPrimitives = [], projectPath, preFetchedManifest) {
14753
14986
  const targetDir = projectPath || process.cwd();
14754
14987
  try {
14755
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14988
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14756
14989
  const manifest = preFetchedManifest ?? (await backupApi.getBackupManifest()).data ?? null;
14757
14990
  if (!manifest || !manifest.files?.length) {
14758
14991
  return {
@@ -14838,17 +15071,17 @@ function writeBackupFilesWithByteCompare(manifest, blobs, targetDir) {
14838
15071
  }
14839
15072
  restoredContents.set(makeRestoredContentsKey(file), content);
14840
15073
  const filePath = resolveBackupFileTarget2(file, targetDir);
14841
- if (fs12.existsSync(filePath)) {
14842
- const existing = fs12.readFileSync(filePath);
15074
+ if (fs13.existsSync(filePath)) {
15075
+ const existing = fs13.readFileSync(filePath);
14843
15076
  if (existing.equals(content)) {
14844
15077
  filesUnchanged++;
14845
15078
  continue;
14846
15079
  }
14847
15080
  }
14848
- fs12.mkdirSync(path11.dirname(filePath), {
15081
+ fs13.mkdirSync(path12.dirname(filePath), {
14849
15082
  recursive: true
14850
15083
  });
14851
- fs12.writeFileSync(filePath, content);
15084
+ fs13.writeFileSync(filePath, content);
14852
15085
  filesWritten++;
14853
15086
  }
14854
15087
  return {
@@ -15590,6 +15823,33 @@ var AgentHandler = class {
15590
15823
  success: false
15591
15824
  };
15592
15825
  }
15826
+ const modelSettings = agent?.modelSettings ?? null;
15827
+ writeProgress("\n\u{1F321}\uFE0F Pushing model settings...");
15828
+ try {
15829
+ const success2 = await this.pushModelSettings({
15830
+ apiKey,
15831
+ agentId
15832
+ }, modelSettings);
15833
+ result.modelSettings = {
15834
+ success: success2
15835
+ };
15836
+ if (success2) {
15837
+ if (modelSettings !== null) {
15838
+ const keys = Object.keys(modelSettings).join(", ");
15839
+ writeSuccess(` \u2705 Model settings pushed (${keys})`);
15840
+ } else {
15841
+ writeSuccess(" \u2705 Model settings cleared (using provider defaults)");
15842
+ }
15843
+ } else {
15844
+ console.error(" \u274C Failed to push model settings");
15845
+ }
15846
+ } catch (error) {
15847
+ if (AuthenticationError.isAuthenticationError(error)) throw error;
15848
+ console.error(` \u274C Failed to push model settings: ${error.message}`);
15849
+ result.modelSettings = {
15850
+ success: false
15851
+ };
15852
+ }
15593
15853
  const batching = agent?.batching ?? null;
15594
15854
  writeProgress("\n\u23F1 Pushing batching configuration...");
15595
15855
  try {
@@ -15723,6 +15983,13 @@ var AgentHandler = class {
15723
15983
  });
15724
15984
  return result.success;
15725
15985
  }
15986
+ async pushModelSettings(ctx, modelSettings) {
15987
+ const agentApi = new AgentApi(BASE_URLS.API, ctx.apiKey);
15988
+ const result = await agentApi.updateAgent(ctx.agentId, {
15989
+ modelSettings
15990
+ });
15991
+ return result.success;
15992
+ }
15726
15993
  async pushBatching(ctx, batchingConfig) {
15727
15994
  const agentApi = new AgentApi(BASE_URLS.API, ctx.apiKey);
15728
15995
  const result = await agentApi.updateAgent(ctx.agentId, {
@@ -16574,7 +16841,7 @@ async function checkAndRestoreBackup(apiKey, agentId, options) {
16574
16841
  if (!confirm) return false;
16575
16842
  }
16576
16843
  try {
16577
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
16844
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
16578
16845
  const urlsResponse = await backupApi.getBlobUrls(manifest.files.map((f) => f.hash));
16579
16846
  if (!urlsResponse.success || !urlsResponse.data) {
16580
16847
  writeError(`Failed to fetch download URLs: ${urlsResponse.error?.message ?? "unknown error"}`);
@@ -16628,6 +16895,7 @@ async function destroyCommand(options) {
16628
16895
  if (options?.force) {
16629
16896
  const deleted2 = deleteApiKey();
16630
16897
  if (deleted2) {
16898
+ invalidateVersioningModeCache();
16631
16899
  writeSuccess("\u2705 API key deleted successfully.");
16632
16900
  } else {
16633
16901
  writeProgress("\u274C Failed to delete API key.");
@@ -16653,6 +16921,7 @@ async function destroyCommand(options) {
16653
16921
  if (confirm) {
16654
16922
  deleted = deleteApiKey();
16655
16923
  if (deleted) {
16924
+ invalidateVersioningModeCache();
16656
16925
  writeSuccess("\u2705 API key deleted successfully.");
16657
16926
  } else {
16658
16927
  writeProgress("\u274C Failed to delete API key.");
@@ -16723,13 +16992,13 @@ __name(apiKeyCommand, "apiKeyCommand");
16723
16992
  init_cli();
16724
16993
  init_files();
16725
16994
  init_auth();
16726
- import path13 from "path";
16995
+ import path14 from "path";
16727
16996
 
16728
16997
  // src/commands/sync.ts
16729
16998
  init_dist();
16730
16999
  init_cli();
16731
- import fs13 from "fs";
16732
- import path12 from "path";
17000
+ import fs14 from "fs";
17001
+ import path13 from "path";
16733
17002
 
16734
17003
  // src/utils/prompt-handler.ts
16735
17004
  init_cli();
@@ -16754,6 +17023,18 @@ async function safePrompt(questions) {
16754
17023
  }
16755
17024
  }
16756
17025
  __name(safePrompt, "safePrompt");
17026
+ async function confirmAction(message) {
17027
+ const answer = await safePrompt([
17028
+ {
17029
+ type: "confirm",
17030
+ name: "confirmed",
17031
+ message,
17032
+ default: false
17033
+ }
17034
+ ]);
17035
+ return answer?.confirmed ?? false;
17036
+ }
17037
+ __name(confirmAction, "confirmAction");
16757
17038
 
16758
17039
  // src/commands/sync.ts
16759
17040
  init_command_utils();
@@ -16940,8 +17221,8 @@ init_mcp_server_handler();
16940
17221
  async function syncCommand(options) {
16941
17222
  return withErrorHandling(async () => {
16942
17223
  const preCompileYaml = readYamlConfig();
16943
- const yamlPath = path12.resolve(process.cwd(), "lua.skill.yaml");
16944
- const preCompileYamlBytes = fs13.existsSync(yamlPath) ? fs13.readFileSync(yamlPath, "utf-8") : null;
17224
+ const yamlPath = path13.resolve(process.cwd(), "lua.skill.yaml");
17225
+ const preCompileYamlBytes = fs14.existsSync(yamlPath) ? fs14.readFileSync(yamlPath, "utf-8") : null;
16945
17226
  let syncCompletedSuccessfully = false;
16946
17227
  try {
16947
17228
  writeProgress("\u{1F504} Compiling to get latest local state...");
@@ -17002,7 +17283,7 @@ async function syncCommand(options) {
17002
17283
  } finally {
17003
17284
  if (!syncCompletedSuccessfully && preCompileYamlBytes !== null) {
17004
17285
  try {
17005
- fs13.writeFileSync(yamlPath, preCompileYamlBytes, "utf-8");
17286
+ fs14.writeFileSync(yamlPath, preCompileYamlBytes, "utf-8");
17006
17287
  } catch {
17007
17288
  }
17008
17289
  }
@@ -18488,6 +18769,7 @@ var VoiceHandler = class extends BaseVersionedHandler {
18488
18769
  if (voice.hasTools !== void 0) body.hasTools = voice.hasTools;
18489
18770
  if (voice.krispEnabled !== void 0) body.krispEnabled = voice.krispEnabled;
18490
18771
  if (voice.persistTranscript !== void 0) body.persistTranscript = voice.persistTranscript;
18772
+ if (voice.onToolFailureSay !== void 0) body.onToolFailureSay = voice.onToolFailureSay;
18491
18773
  if (voice.volume !== void 0) body.volume = voice.volume;
18492
18774
  if (voice.pronunciations !== void 0) body.pronunciations = voice.pronunciations;
18493
18775
  if (voice.backgroundAudio !== void 0) body.backgroundAudio = voice.backgroundAudio;
@@ -18559,7 +18841,7 @@ async function compileCommand(options) {
18559
18841
  }
18560
18842
  writeProgress("\u{1F528} Compiling...");
18561
18843
  const rootDir = process.cwd();
18562
- const outDir = path13.join(rootDir, "dist-v2");
18844
+ const outDir = path14.join(rootDir, "dist-v2");
18563
18845
  const result = await compile({
18564
18846
  rootDir,
18565
18847
  outDir,
@@ -18723,12 +19005,12 @@ init_command_utils();
18723
19005
 
18724
19006
  // src/utils/sandbox.ts
18725
19007
  import vm3 from "vm";
18726
- import path16 from "path";
19008
+ import path17 from "path";
18727
19009
 
18728
19010
  // ../shared-sandbox/dist/index.mjs
18729
19011
  import vm from "vm";
18730
19012
  import { createRequire } from "module";
18731
- import path14 from "path";
19013
+ import path15 from "path";
18732
19014
  import dns from "dns";
18733
19015
  import net from "net";
18734
19016
  import { promisify } from "util";
@@ -18898,7 +19180,7 @@ var REQUIRE_BLOCKLIST = /* @__PURE__ */ new Set([
18898
19180
  var SANDBOX_FAKE_DIRNAME = "/";
18899
19181
  var SANDBOX_FAKE_FILENAME = "/index.ts";
18900
19182
  function buildSandboxRequire(opts) {
18901
- const realRequire = createRequire(path14.join(opts.cwd, "package.json"));
19183
+ const realRequire = createRequire(path15.join(opts.cwd, "package.json"));
18902
19184
  const wrapped = /* @__PURE__ */ __name4((id) => {
18903
19185
  if (REQUIRE_BLOCKLIST.has(id)) {
18904
19186
  const evt = {
@@ -19568,14 +19850,14 @@ __name(runBundleInContext, "runBundleInContext");
19568
19850
  __name4(runBundleInContext, "runBundleInContext");
19569
19851
 
19570
19852
  // src/utils/env-loader.utils.ts
19571
- import path15 from "path";
19572
- import fs14 from "fs";
19853
+ import path16 from "path";
19854
+ import fs15 from "fs";
19573
19855
  function parseEnvFile(filePath) {
19574
- if (!fs14.existsSync(filePath)) {
19856
+ if (!fs15.existsSync(filePath)) {
19575
19857
  return {};
19576
19858
  }
19577
19859
  try {
19578
- const content = fs14.readFileSync(filePath, "utf8");
19860
+ const content = fs15.readFileSync(filePath, "utf8");
19579
19861
  if (!content.trim()) return {};
19580
19862
  const envVars = {};
19581
19863
  content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).forEach((line) => {
@@ -19601,7 +19883,7 @@ async function loadEnvironmentVariables(context) {
19601
19883
  }
19602
19884
  __name(loadEnvironmentVariables, "loadEnvironmentVariables");
19603
19885
  function loadSandboxEnvVariables() {
19604
- const envFilePath = path15.join(process.cwd(), ".env");
19886
+ const envFilePath = path16.join(process.cwd(), ".env");
19605
19887
  const envMap = parseEnvFile(envFilePath);
19606
19888
  return Object.entries(envMap).map(([key, value]) => ({
19607
19889
  key,
@@ -19696,7 +19978,7 @@ function loadEnvironmentVariables2() {
19696
19978
  envVars[key] = value;
19697
19979
  }
19698
19980
  }
19699
- const envFilePath = path16.join(process.cwd(), ".env");
19981
+ const envFilePath = path17.join(process.cwd(), ".env");
19700
19982
  const fileEnvVars = parseEnvFile(envFilePath);
19701
19983
  Object.assign(envVars, fileEnvVars);
19702
19984
  return envVars;
@@ -21582,6 +21864,7 @@ init_dist();
21582
21864
  init_files();
21583
21865
  init_cli();
21584
21866
  init_command_utils();
21867
+ init_auth();
21585
21868
  init_semver();
21586
21869
  init_auth_error();
21587
21870
  init_constants();
@@ -21648,7 +21931,7 @@ async function runBackupPush(opts) {
21648
21931
  projectHash: manifestProjectHash
21649
21932
  };
21650
21933
  }
21651
- const backupApi = new BackupApi(BASE_URLS.API, opts.apiKey, opts.agentId);
21934
+ const backupApi = new backup_api_service_default(BASE_URLS.API, opts.apiKey, opts.agentId);
21652
21935
  const allHashes = opts.fresh ? [
21653
21936
  ...new Set(fileRefs.map((f) => f.hash))
21654
21937
  ] : getAllFileHashes(projectPath);
@@ -21709,8 +21992,8 @@ async function runBackupPush(opts) {
21709
21992
  }
21710
21993
  __name(runBackupPush, "runBackupPush");
21711
21994
  async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency = 10) {
21712
- const fs16 = await import("fs");
21713
- const path18 = await import("path");
21995
+ const fs17 = await import("fs");
21996
+ const path19 = await import("path");
21714
21997
  const zlib2 = await import("zlib");
21715
21998
  const hashToPath = /* @__PURE__ */ new Map();
21716
21999
  for (const f of files) {
@@ -21724,8 +22007,8 @@ async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency =
21724
22007
  if (!rel) {
21725
22008
  throw new Error(`No file found for hash: ${hash}`);
21726
22009
  }
21727
- const abs = path18.join(projectPath, rel);
21728
- const content = fs16.readFileSync(abs);
22010
+ const abs = path19.join(projectPath, rel);
22011
+ const content = fs17.readFileSync(abs);
21729
22012
  const compressed = zlib2.gzipSync(content);
21730
22013
  const response = await fetch(uploadUrls[hash], {
21731
22014
  method: "PUT",
@@ -21816,6 +22099,769 @@ init_analytics();
21816
22099
  init_dist2();
21817
22100
  init_skills_api_service();
21818
22101
 
22102
+ // src/utils/try-git-commit.ts
22103
+ init_cli();
22104
+ init_files();
22105
+
22106
+ // src/utils/git.ts
22107
+ import { spawn } from "child_process";
22108
+ var DEFAULT_TIMEOUT_MS = 1e4;
22109
+ async function runGit(args2, opts = {}) {
22110
+ return new Promise((resolve6, reject) => {
22111
+ const child = spawn("git", args2, {
22112
+ cwd: opts.cwd,
22113
+ shell: false,
22114
+ stdio: [
22115
+ "ignore",
22116
+ "pipe",
22117
+ "pipe"
22118
+ ]
22119
+ });
22120
+ let stdout = "";
22121
+ let stderr = "";
22122
+ let settled = false;
22123
+ const timer = setTimeout(() => {
22124
+ settled = true;
22125
+ child.kill("SIGKILL");
22126
+ reject(new Error(`git ${args2.join(" ")} timed out after ${opts.timeout ?? DEFAULT_TIMEOUT_MS}ms`));
22127
+ }, opts.timeout ?? DEFAULT_TIMEOUT_MS);
22128
+ child.stdout?.on("data", (chunk) => {
22129
+ stdout += chunk.toString();
22130
+ });
22131
+ child.stdout?.on("error", (err) => {
22132
+ if (settled) return;
22133
+ settled = true;
22134
+ clearTimeout(timer);
22135
+ reject(err);
22136
+ });
22137
+ child.stderr?.on("data", (chunk) => {
22138
+ stderr += chunk.toString();
22139
+ });
22140
+ child.stderr?.on("error", () => {
22141
+ });
22142
+ child.on("error", (err) => {
22143
+ if (settled) return;
22144
+ settled = true;
22145
+ clearTimeout(timer);
22146
+ reject(err);
22147
+ });
22148
+ child.on("close", (code) => {
22149
+ if (settled) return;
22150
+ settled = true;
22151
+ clearTimeout(timer);
22152
+ resolve6({
22153
+ stdout,
22154
+ stderr,
22155
+ code: code ?? 0
22156
+ });
22157
+ });
22158
+ });
22159
+ }
22160
+ __name(runGit, "runGit");
22161
+ function classifyGitError(err, stderr = "") {
22162
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
22163
+ return "no-git";
22164
+ }
22165
+ const s = stderr.toLowerCase();
22166
+ if (s.includes("not a git repository")) return "no-repo";
22167
+ if (s.includes("head detached") || s.includes("does not point to a branch") || s.includes("src refspec head does not match")) return "detached-head";
22168
+ if (s.includes("pre-commit hook") || s.includes("hook failed")) return "hook-failed";
22169
+ if (s.includes("already exists")) return "tag-exists";
22170
+ if (s.includes("nothing to commit")) return "nothing-to-commit";
22171
+ if (s.includes("authentication failed") || s.includes("could not read username")) return "auth";
22172
+ return "unknown";
22173
+ }
22174
+ __name(classifyGitError, "classifyGitError");
22175
+ async function isGitAvailable() {
22176
+ try {
22177
+ const { code } = await runGit([
22178
+ "--version"
22179
+ ]);
22180
+ return code === 0;
22181
+ } catch {
22182
+ return false;
22183
+ }
22184
+ }
22185
+ __name(isGitAvailable, "isGitAvailable");
22186
+ async function isInsideGitRepo(cwd) {
22187
+ try {
22188
+ const { code } = await runGit([
22189
+ "rev-parse",
22190
+ "--is-inside-work-tree"
22191
+ ], {
22192
+ cwd
22193
+ });
22194
+ return code === 0;
22195
+ } catch {
22196
+ return false;
22197
+ }
22198
+ }
22199
+ __name(isInsideGitRepo, "isInsideGitRepo");
22200
+ async function gitUserConfigured(cwd) {
22201
+ const readOne = /* @__PURE__ */ __name(async (key) => {
22202
+ try {
22203
+ const { stdout, code } = await runGit([
22204
+ "config",
22205
+ "--get",
22206
+ key
22207
+ ], {
22208
+ cwd
22209
+ });
22210
+ if (code !== 0) return void 0;
22211
+ const trimmed = stdout.trim();
22212
+ return trimmed.length > 0 ? trimmed : void 0;
22213
+ } catch {
22214
+ return void 0;
22215
+ }
22216
+ }, "readOne");
22217
+ const [name, email] = await Promise.all([
22218
+ readOne("user.name"),
22219
+ readOne("user.email")
22220
+ ]);
22221
+ return {
22222
+ name,
22223
+ email
22224
+ };
22225
+ }
22226
+ __name(gitUserConfigured, "gitUserConfigured");
22227
+ async function getCommitSha(cwd) {
22228
+ try {
22229
+ const { stdout, code } = await runGit([
22230
+ "rev-parse",
22231
+ "HEAD"
22232
+ ], {
22233
+ cwd
22234
+ });
22235
+ if (code !== 0) return null;
22236
+ const sha = stdout.trim();
22237
+ return sha.length > 0 ? sha : null;
22238
+ } catch {
22239
+ return null;
22240
+ }
22241
+ }
22242
+ __name(getCommitSha, "getCommitSha");
22243
+
22244
+ // src/utils/try-git-commit.ts
22245
+ init_analytics();
22246
+
22247
+ // src/utils/auto-push-hook.ts
22248
+ init_cli();
22249
+
22250
+ // src/utils/git-auth-store.ts
22251
+ init_constants();
22252
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
22253
+ import { dirname as dirname6 } from "path";
22254
+ function readStore() {
22255
+ try {
22256
+ const raw = readFileSync10(AUTH_STORAGE_FILE, "utf8");
22257
+ const parsed = JSON.parse(raw);
22258
+ return {
22259
+ providers: parsed.providers ?? {}
22260
+ };
22261
+ } catch {
22262
+ return {
22263
+ providers: {}
22264
+ };
22265
+ }
22266
+ }
22267
+ __name(readStore, "readStore");
22268
+ function writeStore(store) {
22269
+ mkdirSync7(dirname6(AUTH_STORAGE_FILE), {
22270
+ recursive: true
22271
+ });
22272
+ writeFileSync7(AUTH_STORAGE_FILE, JSON.stringify(store, null, 2), {
22273
+ mode: 384
22274
+ });
22275
+ }
22276
+ __name(writeStore, "writeStore");
22277
+ async function saveAuth(provider, record) {
22278
+ const store = readStore();
22279
+ store.providers[provider] = record;
22280
+ writeStore(store);
22281
+ }
22282
+ __name(saveAuth, "saveAuth");
22283
+ async function getToken2(provider) {
22284
+ return readStore().providers[provider]?.token ?? null;
22285
+ }
22286
+ __name(getToken2, "getToken");
22287
+ async function getStatus(provider) {
22288
+ const record = readStore().providers[provider];
22289
+ if (!record) return {
22290
+ linked: false
22291
+ };
22292
+ return {
22293
+ linked: true,
22294
+ username: record.username,
22295
+ scopes: record.scopes,
22296
+ linkedAt: record.linkedAt
22297
+ };
22298
+ }
22299
+ __name(getStatus, "getStatus");
22300
+ async function clearAuth(provider) {
22301
+ const store = readStore();
22302
+ delete store.providers[provider];
22303
+ writeStore(store);
22304
+ }
22305
+ __name(clearAuth, "clearAuth");
22306
+
22307
+ // src/utils/git-providers/github-provider.ts
22308
+ import { createServer } from "http";
22309
+ import { randomBytes as randomBytes2 } from "crypto";
22310
+ import { request } from "undici";
22311
+ import open from "open";
22312
+
22313
+ // src/config/git-providers.constants.ts
22314
+ var GITHUB_CLIENT_ID = "Ov23lipe1jgBlCsoi9OO";
22315
+ var GITHUB_OAUTH_BASE_URL = "https://github.com";
22316
+ var GITHUB_API_BASE_URL = "https://api.github.com";
22317
+ var GITHUB_OAUTH_SCOPE = "repo";
22318
+ var LOOPBACK_PORT_MIN = 49152;
22319
+ var LOOPBACK_PORT_MAX = 65535;
22320
+ var LOOPBACK_PORT_ATTEMPTS = 3;
22321
+ var LOOPBACK_TIMEOUT_MS = 5 * 60 * 1e3;
22322
+ var DEVICE_FLOW_DEFAULT_INTERVAL_MS = 5e3;
22323
+ var DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS = 5e3;
22324
+ var PUSH_TIMEOUT_MS = 2 * 60 * 1e3;
22325
+
22326
+ // src/utils/pkce.ts
22327
+ import { createHash as createHash2, randomBytes } from "crypto";
22328
+ function generateCodeVerifier() {
22329
+ return base64url(randomBytes(64));
22330
+ }
22331
+ __name(generateCodeVerifier, "generateCodeVerifier");
22332
+ function computeCodeChallenge(verifier) {
22333
+ return base64url(createHash2("sha256").update(verifier).digest());
22334
+ }
22335
+ __name(computeCodeChallenge, "computeCodeChallenge");
22336
+ function base64url(buf) {
22337
+ return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
22338
+ }
22339
+ __name(base64url, "base64url");
22340
+
22341
+ // src/interfaces/git-providers.ts
22342
+ var GitPushError = class extends Error {
22343
+ static {
22344
+ __name(this, "GitPushError");
22345
+ }
22346
+ status;
22347
+ scrubbedStderr;
22348
+ constructor(message, status, scrubbedStderr) {
22349
+ super(message), this.status = status, this.scrubbedStderr = scrubbedStderr;
22350
+ this.name = "GitPushError";
22351
+ }
22352
+ };
22353
+
22354
+ // src/utils/git-providers/github-provider.ts
22355
+ init_cli();
22356
+
22357
+ // src/utils/scrub-token.ts
22358
+ function scrubToken(s, token) {
22359
+ if (!token) return s;
22360
+ return s.split(token).join("<redacted>");
22361
+ }
22362
+ __name(scrubToken, "scrubToken");
22363
+
22364
+ // src/utils/git-providers/github-provider.ts
22365
+ var SUCCESS_HTML = `<!doctype html><meta charset="utf-8"><title>Lua CLI</title>
22366
+ <body style="font-family: system-ui; padding: 4rem; text-align: center;">
22367
+ <h1>&#x2713; Connected</h1>
22368
+ <p>You can close this tab and return to the terminal.</p>
22369
+ </body>`;
22370
+ var GitHubProvider = class {
22371
+ static {
22372
+ __name(this, "GitHubProvider");
22373
+ }
22374
+ name = "github";
22375
+ async login(opts) {
22376
+ const token = opts.device ? await this.deviceFlow() : await this.loopbackFlow();
22377
+ const username = await this.fetchUsername(token);
22378
+ await saveAuth(this.name, {
22379
+ token,
22380
+ username,
22381
+ scopes: [
22382
+ GITHUB_OAUTH_SCOPE
22383
+ ],
22384
+ linkedAt: (/* @__PURE__ */ new Date()).toISOString()
22385
+ });
22386
+ return {
22387
+ username
22388
+ };
22389
+ }
22390
+ async logout() {
22391
+ await clearAuth(this.name);
22392
+ }
22393
+ async status() {
22394
+ return getStatus(this.name);
22395
+ }
22396
+ async push(opts) {
22397
+ const parsed = parseGitHubHttpsUrl(opts.remoteUrl);
22398
+ const authedUrl = `https://x-access-token:${opts.token}@github.com/${parsed.owner}/${parsed.repo}`;
22399
+ let result;
22400
+ try {
22401
+ result = await runGit([
22402
+ "push",
22403
+ authedUrl,
22404
+ opts.branch
22405
+ ], {
22406
+ cwd: opts.cwd,
22407
+ timeout: PUSH_TIMEOUT_MS
22408
+ });
22409
+ } catch (err) {
22410
+ const raw = err instanceof Error ? err.message : String(err);
22411
+ const scrubbed2 = scrubToken(raw, opts.token);
22412
+ throw new GitPushError(`git push failed: ${scrubbed2}`, 0, scrubbed2);
22413
+ }
22414
+ if (result.code === 0) return;
22415
+ const scrubbed = scrubToken(result.stderr, opts.token);
22416
+ const status = matchPushStatus(scrubbed);
22417
+ throw new GitPushError(status === 401 ? "GitHub authentication failed (token revoked or insufficient scope)." : status === 403 ? "GitHub rejected the push (403 \u2014 possible SSO enforcement or missing scope)." : "git push failed.", status, scrubbed);
22418
+ }
22419
+ async loopbackFlow() {
22420
+ const verifier = generateCodeVerifier();
22421
+ const challenge = computeCodeChallenge(verifier);
22422
+ const state = randomBytes2(16).toString("hex");
22423
+ const { code, redirectUri } = await this.captureLoopbackCode({
22424
+ state,
22425
+ challenge
22426
+ });
22427
+ return this.exchangeCodeForToken({
22428
+ code,
22429
+ verifier,
22430
+ redirectUri
22431
+ });
22432
+ }
22433
+ async captureLoopbackCode(args2) {
22434
+ let lastErr;
22435
+ for (let i = 0; i < LOOPBACK_PORT_ATTEMPTS; i++) {
22436
+ const port = randomPort();
22437
+ try {
22438
+ return await this.runLoopbackServer(port, args2.state, args2.challenge);
22439
+ } catch (err) {
22440
+ lastErr = err;
22441
+ if (!isPortInUse(err)) throw err;
22442
+ }
22443
+ }
22444
+ throw new Error(`Could not bind a loopback port after ${LOOPBACK_PORT_ATTEMPTS} attempts. Try \`lua git auth github --device\` instead.${lastErr ? ` (${lastErr instanceof Error ? lastErr.message : String(lastErr)})` : ""}`);
22445
+ }
22446
+ runLoopbackServer(port, state, challenge) {
22447
+ return new Promise((resolveOuter, rejectOuter) => {
22448
+ let settled = false;
22449
+ const settle = /* @__PURE__ */ __name((fn) => {
22450
+ if (settled) return;
22451
+ settled = true;
22452
+ fn();
22453
+ }, "settle");
22454
+ const server = createServer((req, res) => {
22455
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
22456
+ if (url.pathname !== "/callback") {
22457
+ res.writeHead(404).end();
22458
+ return;
22459
+ }
22460
+ const code = url.searchParams.get("code");
22461
+ const gotState = url.searchParams.get("state");
22462
+ if (!code || gotState !== state) {
22463
+ res.writeHead(400, {
22464
+ "content-type": "text/plain"
22465
+ }).end("State mismatch.");
22466
+ server.close();
22467
+ settle(() => {
22468
+ clearTimeout(timeout);
22469
+ rejectOuter(new Error("OAuth callback state mismatch (possible CSRF). Re-run the command."));
22470
+ });
22471
+ return;
22472
+ }
22473
+ res.writeHead(200, {
22474
+ "content-type": "text/html"
22475
+ }).end(SUCCESS_HTML);
22476
+ server.close();
22477
+ settle(() => {
22478
+ clearTimeout(timeout);
22479
+ resolveOuter({
22480
+ code,
22481
+ redirectUri
22482
+ });
22483
+ });
22484
+ });
22485
+ const timeout = setTimeout(() => {
22486
+ server.close();
22487
+ settle(() => rejectOuter(new Error("OAuth flow timed out (5 min). Re-run `lua git auth github`.")));
22488
+ }, LOOPBACK_TIMEOUT_MS);
22489
+ timeout.unref();
22490
+ server.on("error", (err) => {
22491
+ server.close();
22492
+ settle(() => {
22493
+ clearTimeout(timeout);
22494
+ rejectOuter(err);
22495
+ });
22496
+ });
22497
+ let redirectUri = "";
22498
+ server.listen(port, "127.0.0.1", () => {
22499
+ const actualPort = server.address().port;
22500
+ redirectUri = `http://127.0.0.1:${actualPort}/callback`;
22501
+ const authUrl = new URL(`${GITHUB_OAUTH_BASE_URL}/login/oauth/authorize`);
22502
+ authUrl.searchParams.set("client_id", GITHUB_CLIENT_ID);
22503
+ authUrl.searchParams.set("redirect_uri", redirectUri);
22504
+ authUrl.searchParams.set("scope", GITHUB_OAUTH_SCOPE);
22505
+ authUrl.searchParams.set("code_challenge", challenge);
22506
+ authUrl.searchParams.set("code_challenge_method", "S256");
22507
+ authUrl.searchParams.set("state", state);
22508
+ open(authUrl.toString()).catch(() => {
22509
+ server.close();
22510
+ settle(() => {
22511
+ clearTimeout(timeout);
22512
+ rejectOuter(new Error("Couldn't open a browser \u2014 try `lua git auth github --device` for the device flow."));
22513
+ });
22514
+ });
22515
+ });
22516
+ });
22517
+ }
22518
+ async exchangeCodeForToken(args2) {
22519
+ const res = await request(`${GITHUB_OAUTH_BASE_URL}/login/oauth/access_token`, {
22520
+ method: "POST",
22521
+ headers: {
22522
+ accept: "application/json",
22523
+ "content-type": "application/json"
22524
+ },
22525
+ body: JSON.stringify({
22526
+ client_id: GITHUB_CLIENT_ID,
22527
+ code: args2.code,
22528
+ code_verifier: args2.verifier,
22529
+ redirect_uri: args2.redirectUri,
22530
+ grant_type: "authorization_code"
22531
+ })
22532
+ });
22533
+ const body = await res.body.json();
22534
+ if (!body.access_token) {
22535
+ throw new Error(`GitHub rejected the token exchange: ${body.error ?? "unknown error"}`);
22536
+ }
22537
+ return body.access_token;
22538
+ }
22539
+ async fetchUsername(token) {
22540
+ const res = await request(`${GITHUB_API_BASE_URL}/user`, {
22541
+ method: "GET",
22542
+ headers: {
22543
+ accept: "application/vnd.github+json",
22544
+ authorization: `Bearer ${token}`,
22545
+ "user-agent": "lua-cli"
22546
+ }
22547
+ });
22548
+ const body = await res.body.json();
22549
+ if (!body.login) throw new Error("Could not read GitHub username after auth.");
22550
+ return body.login;
22551
+ }
22552
+ async deviceFlow() {
22553
+ const codeRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/device/code`, {
22554
+ method: "POST",
22555
+ headers: {
22556
+ accept: "application/json",
22557
+ "content-type": "application/json"
22558
+ },
22559
+ body: JSON.stringify({
22560
+ client_id: GITHUB_CLIENT_ID,
22561
+ scope: GITHUB_OAUTH_SCOPE
22562
+ })
22563
+ });
22564
+ const codeBody = await codeRes.body.json();
22565
+ if (!codeBody.device_code || !codeBody.user_code || !codeBody.verification_uri) {
22566
+ throw new Error(`GitHub rejected device-code request: ${codeBody.error ?? "unknown error"}`);
22567
+ }
22568
+ writeInfo(`Open ${codeBody.verification_uri} and enter the code: ${codeBody.user_code}`);
22569
+ let intervalMs = codeBody.interval != null ? codeBody.interval * 1e3 : DEVICE_FLOW_DEFAULT_INTERVAL_MS;
22570
+ const deadline = Date.now() + (codeBody.expires_in ?? 900) * 1e3;
22571
+ while (Date.now() < deadline) {
22572
+ await sleep(intervalMs);
22573
+ if (Date.now() >= deadline) break;
22574
+ const pollRes = await request(`${GITHUB_OAUTH_BASE_URL}/login/oauth/access_token`, {
22575
+ method: "POST",
22576
+ headers: {
22577
+ accept: "application/json",
22578
+ "content-type": "application/json"
22579
+ },
22580
+ body: JSON.stringify({
22581
+ client_id: GITHUB_CLIENT_ID,
22582
+ device_code: codeBody.device_code,
22583
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
22584
+ })
22585
+ });
22586
+ const body = await pollRes.body.json();
22587
+ if (body.access_token) return body.access_token;
22588
+ if (body.error === "authorization_pending") continue;
22589
+ if (body.error === "slow_down") {
22590
+ intervalMs += DEVICE_FLOW_SLOW_DOWN_INCREMENT_MS;
22591
+ continue;
22592
+ }
22593
+ if (body.error === "expired_token") {
22594
+ throw new Error("Device code expired. Re-run `lua git auth github --device`.");
22595
+ }
22596
+ throw new Error(`GitHub device-flow error: ${body.error ?? "unknown"}`);
22597
+ }
22598
+ throw new Error("Device flow timed out. Re-run `lua git auth github --device`.");
22599
+ }
22600
+ };
22601
+ function randomPort() {
22602
+ const span = LOOPBACK_PORT_MAX - LOOPBACK_PORT_MIN + 1;
22603
+ return LOOPBACK_PORT_MIN + Math.floor(Math.random() * span);
22604
+ }
22605
+ __name(randomPort, "randomPort");
22606
+ function isPortInUse(err) {
22607
+ return !!err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE";
22608
+ }
22609
+ __name(isPortInUse, "isPortInUse");
22610
+ function sleep(ms) {
22611
+ return new Promise((r) => setTimeout(r, ms));
22612
+ }
22613
+ __name(sleep, "sleep");
22614
+ function isGitHubUrl(url) {
22615
+ try {
22616
+ const u = new URL(url);
22617
+ return u.protocol === "https:" && u.hostname === "github.com";
22618
+ } catch {
22619
+ return false;
22620
+ }
22621
+ }
22622
+ __name(isGitHubUrl, "isGitHubUrl");
22623
+ function parseGitHubHttpsUrl(url) {
22624
+ let u;
22625
+ try {
22626
+ u = new URL(url);
22627
+ } catch {
22628
+ throw new Error(`Not a parseable URL: ${url}. v1 only supports https GitHub remotes.`);
22629
+ }
22630
+ if (u.protocol !== "https:" || u.hostname !== "github.com") {
22631
+ throw new Error(`Not a GitHub HTTPS remote: ${url}. v1 only supports https://github.com/...`);
22632
+ }
22633
+ const parts = u.pathname.replace(/^\/+/, "").replace(/\.git$/, "").split("/");
22634
+ if (parts.length < 2 || !parts[0] || !parts[1]) {
22635
+ throw new Error(`Unexpected GitHub URL shape: ${url}`);
22636
+ }
22637
+ return {
22638
+ owner: parts[0],
22639
+ repo: parts[1]
22640
+ };
22641
+ }
22642
+ __name(parseGitHubHttpsUrl, "parseGitHubHttpsUrl");
22643
+ function matchPushStatus(stderr) {
22644
+ const s = stderr.toLowerCase();
22645
+ if (s.includes("401") || s.includes("authentication failed")) return 401;
22646
+ if (s.includes("403") || s.includes("permission to") || s.includes("forbidden")) return 403;
22647
+ return 0;
22648
+ }
22649
+ __name(matchPushStatus, "matchPushStatus");
22650
+
22651
+ // src/utils/auto-push-hook.ts
22652
+ init_analytics();
22653
+ var warned = /* @__PURE__ */ new Set();
22654
+ function warnOnce(key, message) {
22655
+ if (warned.has(key)) return;
22656
+ warned.add(key);
22657
+ writeInfo(`\u26A0\uFE0F ${message}`);
22658
+ }
22659
+ __name(warnOnce, "warnOnce");
22660
+ async function tryGitAutoPush(ctx) {
22661
+ try {
22662
+ if (ctx.config?.git?.autoPush !== true) return;
22663
+ const token = await getToken2("github");
22664
+ if (!token) {
22665
+ warnOnce("missing-github-token", "git.autoPush is enabled but no GitHub auth \u2014 run `lua git auth github`.");
22666
+ return;
22667
+ }
22668
+ const remote = await readRemoteOriginUrl(ctx.cwd);
22669
+ if (!remote) {
22670
+ warnOnce("missing-github-remote", "git.autoPush is enabled but no `remote.origin.url` is set. Skipping push.");
22671
+ return;
22672
+ }
22673
+ if (!isGitHubUrl(remote)) {
22674
+ warnOnce("non-github-remote", `git.autoPush is enabled but origin (${remote}) is not a supported GitHub HTTPS URL. v1 only supports https://github.com/<owner>/<repo> \u2014 skipping push.`);
22675
+ return;
22676
+ }
22677
+ const branch = await readCurrentBranch(ctx.cwd);
22678
+ if (!branch) {
22679
+ warnOnce("detached-head", "Cannot auto-push from a detached HEAD. Skipping push.");
22680
+ return;
22681
+ }
22682
+ const provider = new GitHubProvider();
22683
+ try {
22684
+ await provider.push({
22685
+ token,
22686
+ remoteUrl: remote,
22687
+ branch,
22688
+ cwd: ctx.cwd ?? process.cwd()
22689
+ });
22690
+ writeInfo(`\u2713 Pushed ${ctx.commitSha?.slice(0, 7) ?? ""} to ${remote} (${branch})`);
22691
+ trackEvent("cli_auto_push_succeeded", {
22692
+ action: ctx.action
22693
+ });
22694
+ } catch (err) {
22695
+ const status = err instanceof GitPushError ? err.status : 0;
22696
+ const message = err instanceof GitPushError ? err.scrubbedStderr || err.message : err instanceof Error ? err.message : String(err);
22697
+ if (status === 401) {
22698
+ warnOnce("push-revoked", "GitHub auth was revoked \u2014 run `lua git auth github` to re-link. Commit kept locally.");
22699
+ } else if (status === 403) {
22700
+ warnOnce("push-rejected-403", `GitHub rejected the push: ${message}. Commit kept locally.`);
22701
+ } else {
22702
+ warnOnce("push-failed-other", `Auto-push failed: ${message}. Commit kept locally; run \`git push\` to retry.`);
22703
+ }
22704
+ trackEvent("cli_auto_push_failed", {
22705
+ action: ctx.action,
22706
+ status
22707
+ });
22708
+ }
22709
+ } catch {
22710
+ warnOnce("hook-errored", "Auto-push hook errored unexpectedly. Commit kept locally; run `git push` to retry.");
22711
+ trackEvent("cli_auto_push_failed", {
22712
+ action: ctx.action,
22713
+ status: 0
22714
+ });
22715
+ }
22716
+ }
22717
+ __name(tryGitAutoPush, "tryGitAutoPush");
22718
+ async function readRemoteOriginUrl(cwd) {
22719
+ const res = await runGit([
22720
+ "config",
22721
+ "--get",
22722
+ "remote.origin.url"
22723
+ ], {
22724
+ cwd
22725
+ });
22726
+ if (res.code !== 0) return null;
22727
+ const trimmed = res.stdout.trim();
22728
+ return trimmed.length > 0 ? trimmed : null;
22729
+ }
22730
+ __name(readRemoteOriginUrl, "readRemoteOriginUrl");
22731
+ async function readCurrentBranch(cwd) {
22732
+ const res = await runGit([
22733
+ "rev-parse",
22734
+ "--abbrev-ref",
22735
+ "HEAD"
22736
+ ], {
22737
+ cwd
22738
+ });
22739
+ if (res.code !== 0) return null;
22740
+ const trimmed = res.stdout.trim();
22741
+ if (!trimmed || trimmed === "HEAD") return null;
22742
+ return trimmed;
22743
+ }
22744
+ __name(readCurrentBranch, "readCurrentBranch");
22745
+
22746
+ // src/utils/try-git-commit.ts
22747
+ async function tryGitCommit(opts) {
22748
+ const config = readYamlConfig();
22749
+ if (!config?.git?.enabled) {
22750
+ return {
22751
+ committed: false
22752
+ };
22753
+ }
22754
+ const runOpts = {
22755
+ cwd: opts.cwd
22756
+ };
22757
+ if (!opts.allowEmpty) {
22758
+ try {
22759
+ const addResult = await runGit([
22760
+ "add",
22761
+ "-A"
22762
+ ], runOpts);
22763
+ if (addResult.code !== 0) {
22764
+ const reason = classifyGitError(null, addResult.stderr);
22765
+ reportFailure("add", reason);
22766
+ return {
22767
+ committed: false
22768
+ };
22769
+ }
22770
+ } catch (err) {
22771
+ const reason = classifyGitError(err);
22772
+ reportFailure("add", reason);
22773
+ return {
22774
+ committed: false
22775
+ };
22776
+ }
22777
+ }
22778
+ const commitArgs = [
22779
+ "commit"
22780
+ ];
22781
+ if (opts.allowEmpty) commitArgs.push("--allow-empty");
22782
+ commitArgs.push("-m", opts.message);
22783
+ try {
22784
+ const commitResult = await runGit(commitArgs, runOpts);
22785
+ if (commitResult.code !== 0) {
22786
+ const reason = classifyGitError(null, commitResult.stderr);
22787
+ if (reason !== "nothing-to-commit") {
22788
+ reportFailure("commit", reason);
22789
+ }
22790
+ return {
22791
+ committed: false
22792
+ };
22793
+ }
22794
+ } catch (err) {
22795
+ const reason = classifyGitError(err);
22796
+ if (reason !== "nothing-to-commit") {
22797
+ reportFailure("commit", reason);
22798
+ }
22799
+ return {
22800
+ committed: false
22801
+ };
22802
+ }
22803
+ const sha = await getCommitSha(opts.cwd) ?? void 0;
22804
+ let tagged;
22805
+ if (opts.tag) {
22806
+ try {
22807
+ const tagResult = await runGit([
22808
+ "tag",
22809
+ opts.tag
22810
+ ], runOpts);
22811
+ if (tagResult.code !== 0) {
22812
+ const reason = classifyGitError(null, tagResult.stderr);
22813
+ reportFailure("tag", reason);
22814
+ tagged = false;
22815
+ } else {
22816
+ tagged = true;
22817
+ }
22818
+ } catch (err) {
22819
+ const reason = classifyGitError(err);
22820
+ reportFailure("tag", reason);
22821
+ tagged = false;
22822
+ }
22823
+ }
22824
+ trackEvent("cli_git_sideeffect_succeeded", {
22825
+ action: opts.action,
22826
+ has_sha: Boolean(sha),
22827
+ has_tag: tagged === true
22828
+ });
22829
+ await tryGitAutoPush({
22830
+ config,
22831
+ cwd: opts.cwd,
22832
+ commitSha: sha,
22833
+ action: opts.action
22834
+ });
22835
+ return {
22836
+ committed: true,
22837
+ sha,
22838
+ tagged
22839
+ };
22840
+ }
22841
+ __name(tryGitCommit, "tryGitCommit");
22842
+ function reportFailure(action, reason) {
22843
+ writeInfo(`\u26A0\uFE0F Git ${action} skipped: ${reason}`);
22844
+ trackEvent("cli_git_sideeffect_failed", {
22845
+ action,
22846
+ reason
22847
+ });
22848
+ }
22849
+ __name(reportFailure, "reportFailure");
22850
+
22851
+ // src/utils/git-messages.ts
22852
+ var GIT_MESSAGES = {
22853
+ pushStaged: "lua: push staged code",
22854
+ createVersion: /* @__PURE__ */ __name((version, message) => {
22855
+ const trimmed = message?.trim();
22856
+ return trimmed ? `lua: create version v${version}: ${trimmed}` : `lua: create version v${version}`;
22857
+ }, "createVersion"),
22858
+ promote: /* @__PURE__ */ __name((version) => `lua: promote v${version} to production`, "promote"),
22859
+ delete: /* @__PURE__ */ __name((version) => `lua: delete version v${version}`, "delete"),
22860
+ pullLatest: "lua: pull latest backup",
22861
+ pullVersion: /* @__PURE__ */ __name((version) => `lua: pull version v${version}`, "pullVersion")
22862
+ };
22863
+ var tagName = /* @__PURE__ */ __name((version) => `lua/v${version}`, "tagName");
22864
+
21819
22865
  // src/commands/push-helpers.ts
21820
22866
  function pickPushAllNextStepVariant(opts) {
21821
22867
  if (opts.pushedSomething && opts.failedCount === 0) return "success";
@@ -22130,6 +23176,34 @@ async function pushCommand(type, cmdObj) {
22130
23176
  allowAll: true
22131
23177
  });
22132
23178
  }
23179
+ const earlyApiKey = loadApiKey();
23180
+ const earlyConfig = readYamlConfig();
23181
+ const earlyAgentId = earlyConfig?.agent?.agentId;
23182
+ const versioning = earlyApiKey && earlyAgentId ? await getVersioningModeCached(earlyApiKey, earlyAgentId) : {
23183
+ enabled: false
23184
+ };
23185
+ const isStageAll = !type && versioning.enabled;
23186
+ const isGranular = Boolean(type && type !== "all" && versioning.enabled);
23187
+ const isAutoDeployNoOp = versioning.enabled && (type === "all" || !type) && Boolean(options.autoDeploy);
23188
+ if (isAutoDeployNoOp) {
23189
+ writeInfo("\u26A0\uFE0F --auto-deploy is ignored when agent versioning is on. Use `lua version promote <version>` after `lua version create`.");
23190
+ options.autoDeploy = false;
23191
+ }
23192
+ if (options.entityName && !type) {
23193
+ console.log("\nUsage:");
23194
+ console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
23195
+ console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
23196
+ throw new Error("Type must be specified when using the --name option.");
23197
+ }
23198
+ if (isStageAll) {
23199
+ return await pushAllCommand({
23200
+ ...options,
23201
+ autoDeployNoopWarned: isAutoDeployNoOp
23202
+ });
23203
+ }
23204
+ if (isGranular) {
23205
+ writeInfo("\u26A0\uFE0F Granular push is deprecated when agent versioning is on. Use `lua push` (no args) to stage all changes, then `lua version create`.");
23206
+ }
22133
23207
  if (type === "all") {
22134
23208
  if (!options.force) {
22135
23209
  console.log("\nUsage:");
@@ -22137,13 +23211,10 @@ async function pushCommand(type, cmdObj) {
22137
23211
  console.log(" lua push all --force --auto-deploy Push and deploy all to production");
22138
23212
  throw new Error('The "all" type requires the --force flag');
22139
23213
  }
22140
- return await pushAllCommand(options);
22141
- }
22142
- if (options.entityName && !type) {
22143
- console.log("\nUsage:");
22144
- console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
22145
- console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
22146
- throw new Error("Type must be specified when using the --name option.");
23214
+ return await pushAllCommand({
23215
+ ...options,
23216
+ autoDeployNoopWarned: isAutoDeployNoOp
23217
+ });
22147
23218
  }
22148
23219
  if (type) {
22149
23220
  selectedType = type;
@@ -22284,13 +23355,17 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
22284
23355
  bundles_total: versionedPushDeployResult?.bundleStats?.total ?? 0,
22285
23356
  bundles_uploaded: versionedPushDeployResult?.bundleStats?.uploaded ?? 0,
22286
23357
  bundles_existed: versionedPushDeployResult?.bundleStats?.alreadyExisted ?? 0,
22287
- // Phase 2 `--include-source` telemetry. Tracks adoption of the flag
22288
- // and whether the per-skill source attach actually lands cleanly —
22289
- // useful for the eventual "flip default to ON" decision in Slice 5.
23358
+ // `--include-source` telemetry. Tracks adoption of the flag and whether
23359
+ // the per-skill source attach actually lands cleanly — useful for the
23360
+ // eventual "flip default to ON" decision.
22290
23361
  include_source_requested: options.includeSource || false,
22291
23362
  include_source_attempted: trackedIncludeSource?.attempted ?? 0,
22292
23363
  include_source_attached: trackedIncludeSource?.attached ?? 0,
22293
- include_source_failed: trackedIncludeSource?.failed ?? 0
23364
+ include_source_failed: trackedIncludeSource?.failed ?? 0,
23365
+ // Agent-versioning telemetry. Driven by isStageAll / isGranular.
23366
+ versioning_mode: versioning.enabled ? isGranular ? "granular" : "all" : "flag-off",
23367
+ granular_deprecation_warned: isGranular,
23368
+ auto_deploy_noop_warned: isAutoDeployNoOp
22294
23369
  });
22295
23370
  const productionDeployedActually = versionedPushDeployResult?.productionDeploySucceeded === true || !!agentOutcome && !agentOutcome.cancelled && agentOutcome.productionDeployedWithAutoDeploy;
22296
23371
  const personaAutoDeployRanAndFailed = selectedType === "agent" && options.autoDeploy && !!agentOutcome && !agentOutcome.cancelled && "personaAutoDeployFailed" in agentOutcome && agentOutcome.personaAutoDeployFailed;
@@ -22583,6 +23658,7 @@ async function pushAllCommand(options) {
22583
23658
  }
22584
23659
  const apiKey = await requireAuthOrExit(false);
22585
23660
  const agentId = config.agent.agentId;
23661
+ const pushAllVersioning = await getVersioningModeCached(apiKey, agentId);
22586
23662
  let manifest;
22587
23663
  try {
22588
23664
  manifest = loadManifest();
@@ -22879,15 +23955,26 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
22879
23955
  bundles_total: bundleStatsAggregate.total,
22880
23956
  bundles_uploaded: bundleStatsAggregate.uploaded,
22881
23957
  bundles_existed: bundleStatsAggregate.existed,
22882
- // Phase 2 `--include-source` telemetry, parallel to the single-push
23958
+ // `--include-source` telemetry, parallel to the single-push
22883
23959
  // `cli_push_completed` shape. Without these the `lua push all` path —
22884
- // arguably the highest-volume one for multi-skill agents — would be
22885
- // invisible to adoption monitoring.
23960
+ // the highest-volume one for multi-skill agents — would be invisible
23961
+ // to adoption monitoring.
22886
23962
  include_source_requested: options.includeSource || false,
22887
23963
  include_source_attempted: includeSourceStats?.attempted ?? 0,
22888
23964
  include_source_attached: includeSourceStats?.attached ?? 0,
22889
- include_source_failed: includeSourceStats?.failed ?? 0
23965
+ include_source_failed: includeSourceStats?.failed ?? 0,
23966
+ // Agent-versioning telemetry.
23967
+ versioning_mode: pushAllVersioning.enabled ? "stage-all" : "flag-off",
23968
+ granular_deprecation_warned: false,
23969
+ auto_deploy_noop_warned: options.autoDeployNoopWarned || false
22890
23970
  });
23971
+ try {
23972
+ await tryGitCommit({
23973
+ message: GIT_MESSAGES.pushStaged,
23974
+ action: "push-staged"
23975
+ });
23976
+ } catch {
23977
+ }
22891
23978
  const pushedSomething = allResults.length > 0 || mcpPushedCount > 0 || !!personaPushResult || backupSuccess;
22892
23979
  const variant = pickPushAllNextStepVariant({
22893
23980
  pushedSomething,
@@ -22895,7 +23982,11 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
22895
23982
  });
22896
23983
  if (variant === "success") {
22897
23984
  console.log("");
22898
- if (options.autoDeploy) {
23985
+ if (pushAllVersioning.enabled) {
23986
+ if (!options.suppressVersionCreateHint) {
23987
+ writeInfo(`Run \`lua version create [-m "<message>"]\` to snapshot the staged state into a named version, then \`lua version promote <version>\` to deploy.`);
23988
+ }
23989
+ } else if (options.autoDeploy) {
22899
23990
  writeHintBlock({
22900
23991
  headline: "All deployed. Generate test traffic first, then inspect:",
22901
23992
  lines: [
@@ -23163,6 +24254,10 @@ async function deployCommand(type, cmdObj) {
23163
24254
  selectedType = answer.type;
23164
24255
  }
23165
24256
  const apiKey = await requireAuthOrExit();
24257
+ const versioning = await getVersioningModeCached(apiKey, agentId);
24258
+ if (versioning.enabled) {
24259
+ writeInfo("\u26A0\uFE0F `lua deploy` is deprecated when agent versioning is on. Use `lua version promote <version>` for instant, atomic promotion.");
24260
+ }
23166
24261
  let personaDeployed = false;
23167
24262
  let versionedOutcome = null;
23168
24263
  if (selectedType === "persona") {
@@ -23175,7 +24270,8 @@ async function deployCommand(type, cmdObj) {
23175
24270
  primitive_type: selectedType,
23176
24271
  force_mode: options.force || false,
23177
24272
  entity_selected_by_name: !!options.name,
23178
- version_selected_by_flag: !!options.version
24273
+ version_selected_by_flag: !!options.version,
24274
+ granular_deprecation_warned: versioning.enabled
23179
24275
  });
23180
24276
  const deployed = selectedType === "persona" ? personaDeployed : versionedOutcome?.deployed ?? false;
23181
24277
  const hintPrintedAlready = selectedType !== "persona" && !!versionedOutcome?.hintPrinted;
@@ -23669,11 +24765,11 @@ init_cli();
23669
24765
 
23670
24766
  // src/utils/sandbox-storage.ts
23671
24767
  init_constants();
23672
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
23673
- import { dirname as dirname5 } from "path";
23674
- function readStore() {
24768
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
24769
+ import { dirname as dirname7 } from "path";
24770
+ function readStore2() {
23675
24771
  try {
23676
- const raw = readFileSync9(SANDBOX_STORAGE_FILE, "utf8");
24772
+ const raw = readFileSync11(SANDBOX_STORAGE_FILE, "utf8");
23677
24773
  const parsed = JSON.parse(raw);
23678
24774
  return {
23679
24775
  skills: parsed.skills ?? {},
@@ -23690,28 +24786,28 @@ function readStore() {
23690
24786
  };
23691
24787
  }
23692
24788
  }
23693
- __name(readStore, "readStore");
23694
- function writeStore(store) {
24789
+ __name(readStore2, "readStore");
24790
+ function writeStore2(store) {
23695
24791
  try {
23696
- mkdirSync6(dirname5(SANDBOX_STORAGE_FILE), {
24792
+ mkdirSync8(dirname7(SANDBOX_STORAGE_FILE), {
23697
24793
  recursive: true
23698
24794
  });
23699
- writeFileSync6(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
24795
+ writeFileSync8(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
23700
24796
  } catch {
23701
24797
  }
23702
24798
  }
23703
- __name(writeStore, "writeStore");
24799
+ __name(writeStore2, "writeStore");
23704
24800
  async function getSandboxSkillId(skillName) {
23705
- const store = readStore();
24801
+ const store = readStore2();
23706
24802
  const key = skillName ?? "__default__";
23707
24803
  return store.skills[key] ?? null;
23708
24804
  }
23709
24805
  __name(getSandboxSkillId, "getSandboxSkillId");
23710
24806
  async function setSandboxSkillId(sandboxId, skillName) {
23711
- const store = readStore();
24807
+ const store = readStore2();
23712
24808
  const key = skillName ?? "__default__";
23713
24809
  store.skills[key] = sandboxId;
23714
- writeStore(store);
24810
+ writeStore2(store);
23715
24811
  }
23716
24812
  __name(setSandboxSkillId, "setSandboxSkillId");
23717
24813
  async function getAllSandboxSkillIds(yamlConfig) {
@@ -23737,14 +24833,14 @@ async function getAllSandboxSkillIds(yamlConfig) {
23737
24833
  }
23738
24834
  __name(getAllSandboxSkillIds, "getAllSandboxSkillIds");
23739
24835
  async function getSandboxPreProcessorId(preprocessorName) {
23740
- const store = readStore();
24836
+ const store = readStore2();
23741
24837
  return store.preprocessors[preprocessorName] ?? null;
23742
24838
  }
23743
24839
  __name(getSandboxPreProcessorId, "getSandboxPreProcessorId");
23744
24840
  async function setSandboxPreProcessorId(sandboxId, preprocessorName) {
23745
- const store = readStore();
24841
+ const store = readStore2();
23746
24842
  store.preprocessors[preprocessorName] = sandboxId;
23747
- writeStore(store);
24843
+ writeStore2(store);
23748
24844
  }
23749
24845
  __name(setSandboxPreProcessorId, "setSandboxPreProcessorId");
23750
24846
  async function getAllSandboxPreProcessorIds(config) {
@@ -23769,14 +24865,14 @@ async function getAllSandboxPreProcessorIds(config) {
23769
24865
  }
23770
24866
  __name(getAllSandboxPreProcessorIds, "getAllSandboxPreProcessorIds");
23771
24867
  async function getSandboxPostProcessorId(postprocessorName) {
23772
- const store = readStore();
24868
+ const store = readStore2();
23773
24869
  return store.postprocessors[postprocessorName] ?? null;
23774
24870
  }
23775
24871
  __name(getSandboxPostProcessorId, "getSandboxPostProcessorId");
23776
24872
  async function setSandboxPostProcessorId(sandboxId, postprocessorName) {
23777
- const store = readStore();
24873
+ const store = readStore2();
23778
24874
  store.postprocessors[postprocessorName] = sandboxId;
23779
- writeStore(store);
24875
+ writeStore2(store);
23780
24876
  }
23781
24877
  __name(setSandboxPostProcessorId, "setSandboxPostProcessorId");
23782
24878
  async function getAllSandboxPostProcessorIds(config) {
@@ -24200,7 +25296,15 @@ var MIME_TYPES = {
24200
25296
  ".json": "application/json",
24201
25297
  // Email
24202
25298
  ".eml": "message/rfc822",
24203
- ".msg": "application/vnd.ms-outlook"
25299
+ ".msg": "application/vnd.ms-outlook",
25300
+ // Audio. lua-core picks the path per provider: types in the provider's
25301
+ // native allowlist go straight to the model, the rest are transcribed by
25302
+ // Deepgram via FileConversionService.
25303
+ ".m4a": "audio/mp4",
25304
+ ".mp3": "audio/mpeg",
25305
+ ".wav": "audio/wav",
25306
+ ".ogg": "audio/ogg",
25307
+ ".opus": "audio/opus"
24204
25308
  };
24205
25309
  var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
24206
25310
  "image/png",
@@ -25010,8 +26114,8 @@ init_cli();
25010
26114
  init_constants();
25011
26115
  init_command_utils();
25012
26116
  init_developer_api_service();
25013
- import fs15 from "fs";
25014
- import path17 from "path";
26117
+ import fs16 from "fs";
26118
+ import path18 from "path";
25015
26119
  import inquirer10 from "inquirer";
25016
26120
  init_analytics();
25017
26121
  function resolveEnvironment(env, hasNonInteractiveFlags) {
@@ -25370,10 +26474,10 @@ function variablesToEnvContent(variables) {
25370
26474
  }
25371
26475
  __name(variablesToEnvContent, "variablesToEnvContent");
25372
26476
  function saveSandboxEnvVariables(variables) {
25373
- const envFilePath = path17.join(process.cwd(), ".env");
26477
+ const envFilePath = path18.join(process.cwd(), ".env");
25374
26478
  try {
25375
26479
  const content = variablesToEnvContent(variables);
25376
- fs15.writeFileSync(envFilePath, content, "utf8");
26480
+ fs16.writeFileSync(envFilePath, content, "utf8");
25377
26481
  return true;
25378
26482
  } catch (error) {
25379
26483
  console.error("\u274C Error saving .env file:", error);
@@ -27100,7 +28204,7 @@ __name(viewResourceInteractive, "viewResourceInteractive");
27100
28204
  init_cli();
27101
28205
  init_command_utils();
27102
28206
  init_analytics();
27103
- import open from "open";
28207
+ import open2 from "open";
27104
28208
  async function adminCommand() {
27105
28209
  return withErrorHandling(async () => {
27106
28210
  writeProgress("Opening Lua Admin Dashboard...");
@@ -27108,7 +28212,7 @@ async function adminCommand() {
27108
28212
  showProgress: false
27109
28213
  });
27110
28214
  const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
27111
- await open(adminUrl);
28215
+ await open2(adminUrl);
27112
28216
  writeSuccess("Lua Admin Dashboard opened in your browser");
27113
28217
  console.log(`
27114
28218
  Dashboard URL: https://admin.heylua.ai`);
@@ -27124,7 +28228,7 @@ __name(adminCommand, "adminCommand");
27124
28228
  init_cli();
27125
28229
  init_command_utils();
27126
28230
  init_analytics();
27127
- import open2 from "open";
28231
+ import open3 from "open";
27128
28232
  async function evalsCommand() {
27129
28233
  return withErrorHandling(async () => {
27130
28234
  writeProgress("Opening Lua Evaluations Dashboard...");
@@ -27132,7 +28236,7 @@ async function evalsCommand() {
27132
28236
  showProgress: false
27133
28237
  });
27134
28238
  const evalsUrl = `https://evals.heylua.ai?apiKey=${apiKey}&agentID=${agentId}`;
27135
- await open2(evalsUrl);
28239
+ await open3(evalsUrl);
27136
28240
  writeSuccess("Lua Evaluations Dashboard opened in your browser");
27137
28241
  console.log(`
27138
28242
  Dashboard URL: https://evals.heylua.ai`);
@@ -27146,12 +28250,12 @@ __name(evalsCommand, "evalsCommand");
27146
28250
  // src/commands/docs.ts
27147
28251
  init_cli();
27148
28252
  init_analytics();
27149
- import open3 from "open";
28253
+ import open4 from "open";
27150
28254
  async function docsCommand() {
27151
28255
  return withErrorHandling(async () => {
27152
28256
  writeProgress("Opening Lua Documentation...");
27153
28257
  const docsUrl = "https://docs.heylua.ai";
27154
- await open3(docsUrl);
28258
+ await open4(docsUrl);
27155
28259
  writeSuccess("Lua Documentation opened in your browser");
27156
28260
  console.log(`
27157
28261
  Documentation: ${docsUrl}
@@ -27165,7 +28269,7 @@ __name(docsCommand, "docsCommand");
27165
28269
  init_cli();
27166
28270
  init_command_utils();
27167
28271
  import inquirer12 from "inquirer";
27168
- import open4 from "open";
28272
+ import open5 from "open";
27169
28273
 
27170
28274
  // src/api/channels.api.service.ts
27171
28275
  init_http_client();
@@ -27734,7 +28838,7 @@ async function openAdminDashboard(apiKey, config) {
27734
28838
  throw new Error("No orgId found in lua.skill.yaml. Please ensure your configuration is valid.");
27735
28839
  }
27736
28840
  const adminUrl = `https://admin.heylua.ai/validate-token/${apiKey}?redirect=/admin/usage?&agentId=${agentId}&orgId=${orgId}`;
27737
- await open4(adminUrl);
28841
+ await open5(adminUrl);
27738
28842
  writeSuccess("\u2705 Lua Admin Dashboard opened in your browser");
27739
28843
  console.log(`
27740
28844
  Dashboard URL: https://admin.heylua.ai`);
@@ -29865,7 +30969,7 @@ init_auth();
29865
30969
  init_auth_api_service();
29866
30970
  init_files();
29867
30971
  init_artifact_loader();
29868
- import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
30972
+ import { existsSync as existsSync7, readFileSync as readFileSync12 } from "fs";
29869
30973
  import { join as join6 } from "path";
29870
30974
  import { performance } from "perf_hooks";
29871
30975
  import * as os from "os";
@@ -30216,7 +31320,7 @@ function gatherTelemetry() {
30216
31320
  } else {
30217
31321
  try {
30218
31322
  if (existsSync7(TELEMETRY_FILE)) {
30219
- const raw = readFileSync10(TELEMETRY_FILE, "utf8");
31323
+ const raw = readFileSync12(TELEMETRY_FILE, "utf8");
30220
31324
  const cfg = JSON.parse(raw);
30221
31325
  if (typeof cfg.enabled === "boolean") enabled = cfg.enabled;
30222
31326
  }
@@ -36518,7 +37622,7 @@ init_cli();
36518
37622
  init_constants();
36519
37623
  import http from "http";
36520
37624
  import { URL as URL2 } from "url";
36521
- import open5 from "open";
37625
+ import open6 from "open";
36522
37626
  init_command_utils();
36523
37627
  init_developer_api_service();
36524
37628
 
@@ -37541,7 +38645,7 @@ Integration: ${selectedIntegration.name}`);
37541
38645
  `);
37542
38646
  const callbackPromise = startCallbackServer(3e5);
37543
38647
  try {
37544
- await open5(authUrl);
38648
+ await open6(authUrl);
37545
38649
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
37546
38650
  } catch (error) {
37547
38651
  writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
@@ -37972,7 +39076,7 @@ Available scopes for ${selectedIntegration.name}:`);
37972
39076
  `);
37973
39077
  const callbackPromise = startCallbackServer(3e5);
37974
39078
  try {
37975
- await open5(authUrl);
39079
+ await open6(authUrl);
37976
39080
  writeInfo("\u{1F310} Browser opened - please complete the authorization");
37977
39081
  } catch (error) {
37978
39082
  writeInfo("\u{1F4A1} Could not open browser automatically. Please open the URL above manually.");
@@ -39397,7 +40501,7 @@ __name(telemetryCommand, "telemetryCommand");
39397
40501
  init_cli();
39398
40502
  init_command_utils();
39399
40503
  init_analytics();
39400
- import { writeFileSync as writeFileSync7, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
40504
+ import { writeFileSync as writeFileSync9, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
39401
40505
  import { resolve as resolve4, join as join7 } from "path";
39402
40506
  init_artifact_loader();
39403
40507
  init_types();
@@ -39558,7 +40662,7 @@ async function governanceCommand(action) {
39558
40662
  }
39559
40663
  }
39560
40664
  const content = generateFile(setup);
39561
- writeFileSync7(filePath, content, "utf-8");
40665
+ writeFileSync9(filePath, content, "utf-8");
39562
40666
  const relativePath = filePath.replace(process.cwd() + "/", "");
39563
40667
  writeSuccess(`Created ${relativePath}`);
39564
40668
  console.log("");
@@ -39822,14 +40926,14 @@ init_analytics();
39822
40926
  init_command_utils();
39823
40927
  init_artifact_loader();
39824
40928
  init_types();
39825
- import { spawn as spawn2 } from "child_process";
40929
+ import { spawn as spawn3 } from "child_process";
39826
40930
  import { existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
39827
40931
  import { join as join8, relative, resolve as resolve5 } from "path";
39828
40932
  init_voice_api_service();
39829
40933
  init_constants();
39830
40934
 
39831
40935
  // src/commands/voice-terminal.ts
39832
- import { spawn, spawnSync } from "child_process";
40936
+ import { spawn as spawn2, spawnSync } from "child_process";
39833
40937
  import { platform as platform3 } from "os";
39834
40938
  import { AudioFrame, AudioSource, AudioStream, LocalAudioTrack, Room, RoomEvent, TrackKind, TrackPublishOptions, TrackSource } from "@livekit/rtc-node";
39835
40939
 
@@ -40092,7 +41196,7 @@ function spawnCapture() {
40092
41196
  String(FRAME_SAMPLES * 2),
40093
41197
  "-"
40094
41198
  ];
40095
- const proc = spawn("sox", args2, {
41199
+ const proc = spawn2("sox", args2, {
40096
41200
  stdio: [
40097
41201
  "ignore",
40098
41202
  "pipe",
@@ -40123,7 +41227,7 @@ function spawnPlayback() {
40123
41227
  "-",
40124
41228
  "-d"
40125
41229
  ];
40126
- const proc = spawn("sox", args2, {
41230
+ const proc = spawn2("sox", args2, {
40127
41231
  stdio: [
40128
41232
  "pipe",
40129
41233
  "ignore",
@@ -40185,7 +41289,7 @@ __name(pumpRemoteAudioToSpeaker, "pumpRemoteAudioToSpeaker");
40185
41289
  // src/commands/voice-browser.ts
40186
41290
  init_cli();
40187
41291
  import http2 from "http";
40188
- import open6 from "open";
41292
+ import open7 from "open";
40189
41293
  var SAFETY_TIMEOUT_MS = 60 * 60 * 1e3;
40190
41294
  async function runBrowserMode(joinUrl) {
40191
41295
  const { wsUrl, token } = parseJoinUrl(joinUrl);
@@ -40217,7 +41321,7 @@ async function runBrowserMode(joinUrl) {
40217
41321
  const localUrl = `http://localhost:${port}/`;
40218
41322
  writeSuccess(`Voice room ready (opening browser)`);
40219
41323
  console.log(` ${localUrl}`);
40220
- await open6(localUrl);
41324
+ await open7(localUrl);
40221
41325
  await new Promise((resolve6) => {
40222
41326
  const safety = setTimeout(() => {
40223
41327
  try {
@@ -40453,7 +41557,7 @@ function buildRunnerArgs(runner, opts) {
40453
41557
  __name(buildRunnerArgs, "buildRunnerArgs");
40454
41558
  async function spawnRunner(runner, args2, cwd) {
40455
41559
  return new Promise((resolveExit) => {
40456
- const proc = spawn2("npx", [
41560
+ const proc = spawn3("npx", [
40457
41561
  runner,
40458
41562
  ...args2
40459
41563
  ], {
@@ -40846,6 +41950,12 @@ async function sourceRollbackCommand(opts) {
40846
41950
  const config = readYamlConfig();
40847
41951
  const agentId = config?.agent?.agentId;
40848
41952
  if (!agentId) throw new Error("No agentId in lua.skill.yaml. Run `lua init` first.");
41953
+ if (!opts.silent) {
41954
+ const versioning = await getVersioningModeCached(apiKey, agentId);
41955
+ if (versioning.enabled) {
41956
+ writeInfo("\u26A0\uFE0F `lua source rollback` is deprecated when agent versioning is on. Use `lua version promote <version>` \u2014 instant, no re-upload required.");
41957
+ }
41958
+ }
40849
41959
  const fetchM = opts.fetchManifest ?? defaultFetchManifest;
40850
41960
  const fetchU = opts.fetchUrls ?? defaultFetchUrls;
40851
41961
  const dlBlobs = opts.downloadBlobs ?? downloadBlobsParallel;
@@ -40916,6 +42026,631 @@ function defaultRestore(manifest, blobs, targetDir) {
40916
42026
  }
40917
42027
  __name(defaultRestore, "defaultRestore");
40918
42028
 
42029
+ // src/commands/version.ts
42030
+ init_cli();
42031
+ init_command_utils();
42032
+ init_analytics();
42033
+ init_files();
42034
+ init_constants();
42035
+
42036
+ // src/utils/parse-version.ts
42037
+ function parseVersion2(arg) {
42038
+ if (!arg) throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
42039
+ const stripped = arg.replace(/^v/i, "");
42040
+ const n = Number.parseInt(stripped, 10);
42041
+ if (!Number.isFinite(n) || n <= 0 || String(n) !== stripped) {
42042
+ throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
42043
+ }
42044
+ return n;
42045
+ }
42046
+ __name(parseVersion2, "parseVersion");
42047
+
42048
+ // src/commands/version.ts
42049
+ async function assertVersioningEnabled(apiKey, agentId) {
42050
+ const { enabled } = await getVersioningModeCached(apiKey, agentId);
42051
+ if (!enabled) {
42052
+ throw new Error("Agent versioning is not enabled for your organization. Contact your admin to enable it, or use `lua push <primitive>` for the legacy flow.");
42053
+ }
42054
+ }
42055
+ __name(assertVersioningEnabled, "assertVersioningEnabled");
42056
+ async function versionCreateCommand(options = {}) {
42057
+ return withErrorHandling(async () => {
42058
+ const { apiKey, agentId } = await initializeCommand();
42059
+ await assertVersioningEnabled(apiKey, agentId);
42060
+ let config = readYamlConfig();
42061
+ let backupVersion = config?.backup?.activeVersion;
42062
+ if (options.autoPush) {
42063
+ await pushAllCommand({
42064
+ force: true,
42065
+ includeSource: true,
42066
+ suppressVersionCreateHint: true
42067
+ });
42068
+ config = readYamlConfig();
42069
+ backupVersion = config?.backup?.activeVersion;
42070
+ if (backupVersion == null) {
42071
+ throw new Error("Backup tracking lost after push. Aborting.");
42072
+ }
42073
+ }
42074
+ if (backupVersion == null) {
42075
+ throw new Error("No backup exists for this agent. Run `lua push` first, or pass `--auto-push` to push and snapshot in one step.");
42076
+ }
42077
+ let message = options.message;
42078
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
42079
+ const isCi = isCiModeEnabled();
42080
+ if (!message && isTTY && !isCi) {
42081
+ const answers = await safePrompt([
42082
+ {
42083
+ type: "input",
42084
+ name: "input",
42085
+ message: "Optional description (blank to skip):"
42086
+ }
42087
+ ]);
42088
+ message = answers?.input?.trim() || void 0;
42089
+ }
42090
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42091
+ const result = await api.createVersion({
42092
+ message,
42093
+ sourceManifestVersion: backupVersion,
42094
+ commitHash: options.commitHash
42095
+ });
42096
+ if (!result.success || !result.data) {
42097
+ throw new Error(result.error?.message ?? "Failed to create version");
42098
+ }
42099
+ writeInfo(`\u2713 Created v${result.data.version} (staged). Run \`lua version promote v${result.data.version}\` to deploy.`);
42100
+ try {
42101
+ const gitResult = await tryGitCommit({
42102
+ message: GIT_MESSAGES.createVersion(result.data.version, message),
42103
+ action: "version-create",
42104
+ tag: tagName(result.data.version),
42105
+ allowEmpty: true
42106
+ });
42107
+ if (gitResult.committed && gitResult.sha) {
42108
+ await api.patchCommitHash(result.data.version, gitResult.sha);
42109
+ }
42110
+ } catch {
42111
+ }
42112
+ trackEvent("cli_version_create_completed", {
42113
+ auto_push: Boolean(options.autoPush),
42114
+ has_message: Boolean(message),
42115
+ non_interactive: !isTTY || isCi
42116
+ });
42117
+ }, "version create");
42118
+ }
42119
+ __name(versionCreateCommand, "versionCreateCommand");
42120
+ async function versionListCommand(options = {}) {
42121
+ return withErrorHandling(async () => {
42122
+ const { apiKey, agentId } = await initializeCommand();
42123
+ await assertVersioningEnabled(apiKey, agentId);
42124
+ const query = {};
42125
+ if (options.all) query.all = true;
42126
+ if (options.limit != null) query.limit = options.limit;
42127
+ if (options.status) query.status = options.status;
42128
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42129
+ const response = await api.listVersions(query);
42130
+ if (!response.success || !response.data) {
42131
+ throw new Error(response.error?.message ?? "Failed to list versions");
42132
+ }
42133
+ const versions = response.data;
42134
+ if (options.json) {
42135
+ console.log(JSON.stringify(versions, null, 2));
42136
+ } else if (versions.length === 0) {
42137
+ writeInfo("(no versions yet \u2014 run `lua version create` to make one)");
42138
+ } else {
42139
+ console.log("VERSION STATUS CREATED BY MESSAGE");
42140
+ for (const v of versions) {
42141
+ const star = v.status === "active" ? "*" : " ";
42142
+ const date = new Date(v.createdAt).toISOString().slice(0, 16).replace("T", " ");
42143
+ const status = v.status.padEnd(11);
42144
+ const by = (v.createdBy || "").slice(0, 10).padEnd(10);
42145
+ const msg = (v.message || "").slice(0, 60);
42146
+ console.log(`v${v.version}${star} ${status} ${date} ${by}${msg}`);
42147
+ }
42148
+ }
42149
+ trackEvent("cli_version_list_completed", {
42150
+ count: versions.length,
42151
+ status_filter: options.status ?? "all"
42152
+ });
42153
+ }, "version list");
42154
+ }
42155
+ __name(versionListCommand, "versionListCommand");
42156
+ async function versionShowCommand(versionArg, options = {}) {
42157
+ return withErrorHandling(async () => {
42158
+ const { apiKey, agentId } = await initializeCommand();
42159
+ await assertVersioningEnabled(apiKey, agentId);
42160
+ const version = parseVersion2(versionArg);
42161
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42162
+ const response = await api.getVersion(version);
42163
+ if (!response.success || !response.data) {
42164
+ throw new Error(response.error?.message ?? `Version v${version} not found`);
42165
+ }
42166
+ const v = response.data;
42167
+ if (options.json) {
42168
+ console.log(JSON.stringify(v, null, 2));
42169
+ } else {
42170
+ console.log(`Version v${v.version} (${v.status})`);
42171
+ console.log(` Created: ${v.createdAt} by ${v.createdBy}`);
42172
+ if (v.message) console.log(` Message: ${v.message}`);
42173
+ if (v.commitHash) console.log(` Commit: ${v.commitHash}`);
42174
+ console.log(` Model: ${v.snapshot.model}`);
42175
+ console.log(` Snapshot:`);
42176
+ console.log(` Skills: ${v.snapshot.skills.length}`);
42177
+ console.log(` Webhooks: ${v.snapshot.webhooks.length}`);
42178
+ console.log(` Jobs: ${v.snapshot.jobs.length}`);
42179
+ console.log(` Preprocessors: ${v.snapshot.preprocessors.length}`);
42180
+ console.log(` Postprocessors: ${v.snapshot.postprocessors.length}`);
42181
+ console.log(` MCP servers: ${v.snapshot.mcpServers.length}`);
42182
+ console.log(` Persona: ${v.snapshot.persona.versionId || "(none)"}`);
42183
+ }
42184
+ trackEvent("cli_version_show_completed", {
42185
+ version,
42186
+ json: Boolean(options.json)
42187
+ });
42188
+ }, "version show");
42189
+ }
42190
+ __name(versionShowCommand, "versionShowCommand");
42191
+ async function versionDiffCommand(fromArg, toArg, options = {}) {
42192
+ return withErrorHandling(async () => {
42193
+ const { apiKey, agentId } = await initializeCommand();
42194
+ await assertVersioningEnabled(apiKey, agentId);
42195
+ const from = parseVersion2(fromArg);
42196
+ const to = parseVersion2(toArg);
42197
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42198
+ const response = await api.diffVersions(from, to);
42199
+ if (!response.success || !response.data) {
42200
+ throw new Error(response.error?.message ?? "Failed to diff versions");
42201
+ }
42202
+ const diff = response.data;
42203
+ if (options.json) {
42204
+ console.log(JSON.stringify(diff, null, 2));
42205
+ } else {
42206
+ console.log(`Diff v${from} \u2192 v${to}`);
42207
+ const sections = [
42208
+ {
42209
+ name: "Skills",
42210
+ entry: diff.skills,
42211
+ key: "skillId"
42212
+ },
42213
+ {
42214
+ name: "Webhooks",
42215
+ entry: diff.webhooks,
42216
+ key: "webhookId"
42217
+ },
42218
+ {
42219
+ name: "Jobs",
42220
+ entry: diff.jobs,
42221
+ key: "jobId"
42222
+ },
42223
+ {
42224
+ name: "Preprocessors",
42225
+ entry: diff.preprocessors,
42226
+ key: "id"
42227
+ },
42228
+ {
42229
+ name: "Postprocessors",
42230
+ entry: diff.postprocessors,
42231
+ key: "id"
42232
+ }
42233
+ ];
42234
+ for (const { name, entry, key } of sections) {
42235
+ const total = entry.added.length + entry.removed.length + entry.changed.length;
42236
+ if (total === 0) {
42237
+ console.log(`${name}: (no changes)`);
42238
+ continue;
42239
+ }
42240
+ console.log(`${name}:`);
42241
+ for (const a of entry.added) {
42242
+ console.log(` + ${a[key]}@${a.version} (added)`);
42243
+ }
42244
+ for (const r of entry.removed) {
42245
+ console.log(` - ${r[key]}@${r.version} (removed)`);
42246
+ }
42247
+ for (const c of entry.changed) {
42248
+ console.log(` ~ ${c[key]} ${c.from.version} \u2192 ${c.to.version}`);
42249
+ }
42250
+ }
42251
+ const mcp = diff.mcpServers;
42252
+ if (mcp.added.length || mcp.removed.length || mcp.changed.length) {
42253
+ console.log("MCP servers:");
42254
+ mcp.added.forEach((m) => console.log(` + ${m.id} (added)`));
42255
+ mcp.removed.forEach((m) => console.log(` - ${m.id} (removed)`));
42256
+ mcp.changed.forEach((m) => console.log(` ~ ${m.id} (config changed)`));
42257
+ } else {
42258
+ console.log("MCP servers: (no changes)");
42259
+ }
42260
+ console.log(`Persona: ${diff.persona ? `${diff.persona.from} \u2192 ${diff.persona.to}` : "(unchanged)"}`);
42261
+ console.log(`Model: ${diff.model ? `${diff.model.from} \u2192 ${diff.model.to}` : "(unchanged)"}`);
42262
+ }
42263
+ trackEvent("cli_version_diff_completed", {
42264
+ from_to_distance: Math.abs(to - from)
42265
+ });
42266
+ }, "version diff");
42267
+ }
42268
+ __name(versionDiffCommand, "versionDiffCommand");
42269
+ async function versionPromoteCommand(versionArg) {
42270
+ return withErrorHandling(async () => {
42271
+ const { apiKey, agentId } = await initializeCommand();
42272
+ await assertVersioningEnabled(apiKey, agentId);
42273
+ const version = parseVersion2(versionArg);
42274
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42275
+ const response = await api.promoteVersion(version);
42276
+ if (!response.success) {
42277
+ const errCode = response.error?.code;
42278
+ if (errCode === "VERSION_ALREADY_ACTIVE") {
42279
+ writeInfo(`v${version} is already active. No change.`);
42280
+ return;
42281
+ }
42282
+ throw new Error(response.error?.message ?? `Failed to promote v${version}`);
42283
+ }
42284
+ if (!response.data) {
42285
+ throw new Error(`Promote returned no data`);
42286
+ }
42287
+ const { promoted, previousActive } = response.data;
42288
+ const rollback = previousActive != null && previousActive > promoted;
42289
+ if (previousActive == null) {
42290
+ writeInfo(`\u2713 Promoted v${promoted}. (No previous active version.)`);
42291
+ } else {
42292
+ writeInfo(`\u2713 Promoted v${promoted}. Previous active: v${previousActive}.`);
42293
+ }
42294
+ try {
42295
+ await tryGitCommit({
42296
+ message: GIT_MESSAGES.promote(promoted),
42297
+ action: "version-promote",
42298
+ allowEmpty: true
42299
+ });
42300
+ } catch {
42301
+ }
42302
+ trackEvent("cli_version_promote_completed", {
42303
+ from_version: previousActive,
42304
+ to_version: promoted,
42305
+ rollback
42306
+ });
42307
+ }, "version promote");
42308
+ }
42309
+ __name(versionPromoteCommand, "versionPromoteCommand");
42310
+ async function versionDeleteCommand(versionArg, options = {}) {
42311
+ return withErrorHandling(async () => {
42312
+ const { apiKey, agentId } = await initializeCommand();
42313
+ await assertVersioningEnabled(apiKey, agentId);
42314
+ if (versionArg === "current" || versionArg === "active") {
42315
+ throw new Error(`Cannot delete the active version directly. Use \`lua version promote <other>\` to roll back first, then delete.`);
42316
+ }
42317
+ const version = parseVersion2(versionArg);
42318
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
42319
+ const isCi = isCiModeEnabled();
42320
+ if (!options.force && isTTY && !isCi) {
42321
+ const answer = await safePrompt([
42322
+ {
42323
+ type: "confirm",
42324
+ name: "confirm",
42325
+ message: `Delete v${version}?`,
42326
+ default: false
42327
+ }
42328
+ ]);
42329
+ if (!answer?.confirm) {
42330
+ writeInfo("Aborted.");
42331
+ return;
42332
+ }
42333
+ }
42334
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42335
+ const response = await api.deleteVersion(version);
42336
+ if (!response.success) {
42337
+ const errCode = response.error?.code;
42338
+ if (errCode === "CANNOT_DELETE_ACTIVE_VERSION") {
42339
+ throw new Error(`Cannot delete v${version}: it is the active version. Promote a different version first.`);
42340
+ }
42341
+ if (errCode === "CANNOT_DELETE_LAST_VERSION") {
42342
+ throw new Error(`Cannot delete v${version}: it is the only remaining version.`);
42343
+ }
42344
+ throw new Error(response.error?.message ?? `Failed to delete v${version}`);
42345
+ }
42346
+ writeInfo(`\u2713 Deleted v${version}.`);
42347
+ try {
42348
+ await tryGitCommit({
42349
+ message: GIT_MESSAGES.delete(version),
42350
+ action: "version-delete",
42351
+ allowEmpty: true
42352
+ });
42353
+ } catch {
42354
+ }
42355
+ trackEvent("cli_version_delete_completed", {
42356
+ version,
42357
+ force: Boolean(options.force)
42358
+ });
42359
+ }, "version delete");
42360
+ }
42361
+ __name(versionDeleteCommand, "versionDeleteCommand");
42362
+
42363
+ // src/commands/git.ts
42364
+ init_cli();
42365
+ init_files();
42366
+ init_analytics();
42367
+ var NO_YAML_MESSAGE = "No lua.skill.yaml found. Please run this command from a skill directory.";
42368
+ function failConnect(status, banner, errorMessage) {
42369
+ if (banner) writeError(banner);
42370
+ trackEvent("cli_git_connect_completed", {
42371
+ ...status,
42372
+ succeeded: false
42373
+ });
42374
+ throw new Error(errorMessage);
42375
+ }
42376
+ __name(failConnect, "failConnect");
42377
+ async function gitConnectCommand() {
42378
+ return withErrorHandling(async () => {
42379
+ const config = readYamlConfig();
42380
+ if (!config) {
42381
+ failConnect({
42382
+ git_available: false,
42383
+ in_repo: false,
42384
+ user_configured: false
42385
+ }, null, NO_YAML_MESSAGE);
42386
+ }
42387
+ if (!await isGitAvailable()) {
42388
+ failConnect({
42389
+ git_available: false,
42390
+ in_repo: false,
42391
+ user_configured: false
42392
+ }, "\u2717 Git binary not found on PATH.\n Fix: install git from https://git-scm.com/downloads, then re-run `lua git connect`.", "git is not installed");
42393
+ }
42394
+ if (!await isInsideGitRepo()) {
42395
+ failConnect({
42396
+ git_available: true,
42397
+ in_repo: false,
42398
+ user_configured: false
42399
+ }, "\u2717 This directory is not inside a git repository.\n Fix: run `git init` in your project root, then re-run `lua git connect`.", "not inside a git repository");
42400
+ }
42401
+ const identity = await gitUserConfigured();
42402
+ if (!identity.email) {
42403
+ failConnect({
42404
+ git_available: true,
42405
+ in_repo: true,
42406
+ user_configured: false
42407
+ }, '\u2717 Git user.email is not configured.\n Fix: git config --global user.email "you@example.com"', "git user.email is not configured");
42408
+ }
42409
+ if (!identity.name) {
42410
+ failConnect({
42411
+ git_available: true,
42412
+ in_repo: true,
42413
+ user_configured: false
42414
+ }, '\u2717 Git user.name is not configured.\n Fix: git config --global user.name "Your Name"', "git user.name is not configured");
42415
+ }
42416
+ config.git = {
42417
+ enabled: true
42418
+ };
42419
+ writeYamlConfig(config);
42420
+ writeSuccess("\u2713 Git integration enabled. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit.");
42421
+ trackEvent("cli_git_connect_completed", {
42422
+ git_available: true,
42423
+ in_repo: true,
42424
+ user_configured: true,
42425
+ succeeded: true
42426
+ });
42427
+ }, "git connect");
42428
+ }
42429
+ __name(gitConnectCommand, "gitConnectCommand");
42430
+ async function gitDisconnectCommand() {
42431
+ return withErrorHandling(async () => {
42432
+ const config = readYamlConfig();
42433
+ if (!config) {
42434
+ throw new Error(NO_YAML_MESSAGE);
42435
+ }
42436
+ config.git = {
42437
+ enabled: false
42438
+ };
42439
+ writeYamlConfig(config);
42440
+ writeSuccess("\u2713 Git integration disabled. Existing commits and tags are untouched.");
42441
+ trackEvent("cli_git_disconnect_completed", {});
42442
+ }, "git disconnect");
42443
+ }
42444
+ __name(gitDisconnectCommand, "gitDisconnectCommand");
42445
+ async function gitStatusCommand() {
42446
+ return withErrorHandling(async () => {
42447
+ const config = readYamlConfig();
42448
+ const enabled = Boolean(config?.git?.enabled);
42449
+ if (!enabled) {
42450
+ writeInfo("Git integration: disabled (run `lua git connect` to enable)");
42451
+ trackEvent("cli_git_status_completed", {
42452
+ enabled: false
42453
+ });
42454
+ return;
42455
+ }
42456
+ const settled = await Promise.allSettled([
42457
+ runGit([
42458
+ "--version"
42459
+ ]),
42460
+ gitUserConfigured(),
42461
+ runGit([
42462
+ "log",
42463
+ "--grep",
42464
+ "^lua: ",
42465
+ "-n",
42466
+ "1",
42467
+ "--format=%h %s"
42468
+ ]),
42469
+ // tryGitCommit creates lightweight tags (`git tag <name>`), which have
42470
+ // no taggerdate field. creatordate works for both flavors and returns
42471
+ // the committerdate of the referenced commit — chronological order
42472
+ // matches our pattern of tagging immediately after the commit.
42473
+ runGit([
42474
+ "for-each-ref",
42475
+ "--sort=-creatordate",
42476
+ "refs/tags/lua/",
42477
+ "--count=1",
42478
+ "--format=%(refname:short)"
42479
+ ])
42480
+ ]);
42481
+ const versionResult = settled[0].status === "fulfilled" ? settled[0].value : {
42482
+ stdout: "",
42483
+ stderr: "",
42484
+ code: 1
42485
+ };
42486
+ const identity = settled[1].status === "fulfilled" ? settled[1].value : {
42487
+ name: void 0,
42488
+ email: void 0
42489
+ };
42490
+ const lastCommitResult = settled[2].status === "fulfilled" ? settled[2].value : {
42491
+ stdout: "",
42492
+ stderr: "",
42493
+ code: 1
42494
+ };
42495
+ const lastTagResult = settled[3].status === "fulfilled" ? settled[3].value : {
42496
+ stdout: "",
42497
+ stderr: "",
42498
+ code: 1
42499
+ };
42500
+ const versionLine = versionResult.code === 0 ? `${versionResult.stdout.trim()} (\u2713)` : "(not detected)";
42501
+ const identityLine = identity.name && identity.email ? `${identity.name} <${identity.email}> (\u2713)` : "(not configured)";
42502
+ const lastCommit = lastCommitResult.code === 0 && lastCommitResult.stdout.trim() ? lastCommitResult.stdout.trim() : "(none yet)";
42503
+ const lastTag = lastTagResult.code === 0 && lastTagResult.stdout.trim() ? lastTagResult.stdout.trim() : "(none yet)";
42504
+ writeInfo(`Git integration: enabled`);
42505
+ writeInfo(`Project repo: ${process.cwd()}`);
42506
+ writeInfo(`Git binary: ${versionLine}`);
42507
+ writeInfo(`User identity: ${identityLine}`);
42508
+ writeInfo(`Last lua commit: ${lastCommit}`);
42509
+ writeInfo(`Last lua tag: ${lastTag}`);
42510
+ trackEvent("cli_git_status_completed", {
42511
+ enabled: true
42512
+ });
42513
+ }, "git status");
42514
+ }
42515
+ __name(gitStatusCommand, "gitStatusCommand");
42516
+
42517
+ // src/commands/git-auth.ts
42518
+ init_cli();
42519
+ init_analytics();
42520
+ function gitAuthGithubCommand(opts) {
42521
+ return withErrorHandling(async () => {
42522
+ const provider = new GitHubProvider();
42523
+ const existing = await provider.status();
42524
+ if (existing.linked && !opts.force) {
42525
+ const ok = await confirmAction(`Already linked to GitHub as @${existing.username}. Re-link?`);
42526
+ if (!ok) {
42527
+ writeInfo("Aborted. Existing link preserved.");
42528
+ return;
42529
+ }
42530
+ }
42531
+ const result = await provider.login({
42532
+ device: opts.device,
42533
+ force: opts.force
42534
+ });
42535
+ writeSuccess(`Logged in to GitHub as @${result.username}.`);
42536
+ trackEvent("cli_git_auth_succeeded", {
42537
+ provider: "github",
42538
+ flow: opts.device ? "device" : "loopback"
42539
+ });
42540
+ }, "git-auth-github");
42541
+ }
42542
+ __name(gitAuthGithubCommand, "gitAuthGithubCommand");
42543
+ function gitAuthStatusCommand() {
42544
+ return withErrorHandling(async () => {
42545
+ const provider = new GitHubProvider();
42546
+ const s = await provider.status();
42547
+ if (!s.linked) {
42548
+ console.log("Not linked. Run `lua git auth github` to link.");
42549
+ } else {
42550
+ console.log(`Linked: github \u2014 @${s.username} (scopes: ${s.scopes.join(", ")}) \u2014 linked ${formatLinkedAt(s.linkedAt)}`);
42551
+ }
42552
+ trackEvent("cli_git_auth_status_completed", {
42553
+ linked: s.linked
42554
+ });
42555
+ }, "git-auth-status");
42556
+ }
42557
+ __name(gitAuthStatusCommand, "gitAuthStatusCommand");
42558
+ function gitAuthDisconnectCommand(provider) {
42559
+ return withErrorHandling(async () => {
42560
+ const name = (provider ?? "github").toLowerCase();
42561
+ if (name !== "github") {
42562
+ writeError(`Unknown provider: ${name}. v1 only supports github.`);
42563
+ return;
42564
+ }
42565
+ const p = new GitHubProvider();
42566
+ const s = await p.status();
42567
+ if (!s.linked) {
42568
+ console.log(`Not linked to ${name}. Nothing to do.`);
42569
+ return;
42570
+ }
42571
+ await p.logout();
42572
+ writeSuccess(`Disconnected from ${name}.`);
42573
+ trackEvent("cli_git_auth_disconnect_completed", {
42574
+ provider: name
42575
+ });
42576
+ }, "git-auth-disconnect");
42577
+ }
42578
+ __name(gitAuthDisconnectCommand, "gitAuthDisconnectCommand");
42579
+ function formatLinkedAt(iso) {
42580
+ try {
42581
+ return new Date(iso).toISOString().replace("T", " ").replace(/\..*$/, " UTC");
42582
+ } catch {
42583
+ return iso;
42584
+ }
42585
+ }
42586
+ __name(formatLinkedAt, "formatLinkedAt");
42587
+
42588
+ // src/commands/pull.ts
42589
+ init_cli();
42590
+ init_command_utils();
42591
+ init_analytics();
42592
+ init_backup_api_service();
42593
+ init_constants();
42594
+ async function pullCommand(options = {}) {
42595
+ return withErrorHandling(async () => {
42596
+ const { apiKey, agentId } = await initializeCommand();
42597
+ const force = Boolean(options.force);
42598
+ if (options.version) {
42599
+ await assertVersioningEnabled(apiKey, agentId);
42600
+ const version = parseVersion2(options.version);
42601
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
42602
+ const response = await api.getVersion(version);
42603
+ if (!response.success || !response.data) {
42604
+ throw new Error(response.error?.message ?? `Version v${version} not found`);
42605
+ }
42606
+ const sourceManifestVersion = response.data.sourceManifestVersion;
42607
+ if (sourceManifestVersion == null) {
42608
+ throw new Error(`Version v${version} has no source backup linked. Cannot pull source for this version.`);
42609
+ }
42610
+ writeInfo(`Pulling source for agent version v${version} (backup v${sourceManifestVersion})\u2026`);
42611
+ await sourceRollbackCommand({
42612
+ version: sourceManifestVersion,
42613
+ force,
42614
+ silent: true
42615
+ });
42616
+ try {
42617
+ await tryGitCommit({
42618
+ message: GIT_MESSAGES.pullVersion(version),
42619
+ action: "pull-version"
42620
+ });
42621
+ } catch {
42622
+ }
42623
+ trackEvent("cli_pull_completed", {
42624
+ has_version_arg: true
42625
+ });
42626
+ return;
42627
+ }
42628
+ const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
42629
+ const backupResp = await backupApi.getBackupVersions();
42630
+ if (!backupResp.success || !backupResp.data?.versions?.length) {
42631
+ throw new Error("No backup versions available to pull.");
42632
+ }
42633
+ const latest = backupResp.data.versions[0].version;
42634
+ writeInfo(`Pulling latest source backup (v${latest})\u2026`);
42635
+ await sourceRollbackCommand({
42636
+ version: latest,
42637
+ force,
42638
+ silent: true
42639
+ });
42640
+ try {
42641
+ await tryGitCommit({
42642
+ message: GIT_MESSAGES.pullLatest,
42643
+ action: "pull-latest"
42644
+ });
42645
+ } catch {
42646
+ }
42647
+ trackEvent("cli_pull_completed", {
42648
+ has_version_arg: false
42649
+ });
42650
+ }, "pull");
42651
+ }
42652
+ __name(pullCommand, "pullCommand");
42653
+
40919
42654
  // src/cli/command-definitions.ts
40920
42655
  init_cli();
40921
42656
  function setupAuthCommands(program2) {
@@ -41544,6 +43279,59 @@ Examples:
41544
43279
  $ lua voice test --runner vitest Force vitest
41545
43280
  `).action(voiceTestCommand);
41546
43281
  voice.command("list").description("List LuaVoice primitives in the compiled manifest").option("--json", "Output as JSON").action(voiceListCommand);
43282
+ const versionGroup = program2.command("version").description("\u{1F3F7}\uFE0F Manage agent versions \u2014 atomic snapshots (requires agentVersioningEnabled for your org)").addHelpText("after", `
43283
+ Note:
43284
+ All \`lua version\` subcommands require the \`agentVersioningEnabled\`
43285
+ feature flag for your org. If a subcommand errors with "Agent versioning
43286
+ is not enabled," contact your admin to enable it.
43287
+ `);
43288
+ versionGroup.command("create").description("Snapshot current staged state into a new version").option("-m, --message <message>", "Optional description for this version").option("--auto-push", "Push first if local changes are not yet staged").option("--commit-hash <hash>", "Optional git commit hash to associate with this version").addHelpText("after", `
43289
+ Examples:
43290
+ $ lua version create Snapshot current state
43291
+ $ lua version create -m "Add FAQ skill" Include a description
43292
+ $ lua version create --auto-push Push then snapshot in one step
43293
+ `).action((opts) => versionCreateCommand(opts));
43294
+ versionGroup.command("list").description("List versions of the current agent").option("--all", "Show all versions (no server-side cap)").option("--limit <n>", "Cap output to this many entries", (v) => parseInt(v, 10)).option("--status <status>", "Filter by status: active | staged | all", "all").option("--json", "Output as JSON").addHelpText("after", `
43295
+ Examples:
43296
+ $ lua version list List all versions (default)
43297
+ $ lua version list --status active Show only active versions
43298
+ $ lua version list --limit 10 Show the 10 most recent
43299
+ $ lua version list --json Machine-readable output
43300
+ `).action((opts) => versionListCommand(opts));
43301
+ versionGroup.command("show <version>").description("Show full snapshot of a specific version").option("--json", "Output as JSON").addHelpText("after", `
43302
+ Examples:
43303
+ $ lua version show 3 Show version 3 details
43304
+ $ lua version show v3 --json JSON output
43305
+ `).action((version, opts) => versionShowCommand(version, opts));
43306
+ versionGroup.command("diff <from> <to>").description("Compare two versions side-by-side").option("--json", "Output diff as JSON").addHelpText("after", `
43307
+ Examples:
43308
+ $ lua version diff 2 3 Compare v2 \u2192 v3
43309
+ $ lua version diff v2 v3 --json Machine-readable diff
43310
+ `).action((from, to, opts) => versionDiffCommand(from, to, opts));
43311
+ versionGroup.command("promote <version>").description("Promote a version to active (atomic, instant)").addHelpText("after", `
43312
+ Examples:
43313
+ $ lua version promote 3 Promote v3 to the active version
43314
+ $ lua version promote v3 Same (v-prefix accepted)
43315
+ `).action((version) => versionPromoteCommand(version));
43316
+ versionGroup.command("delete <version>").description("Soft-delete a version").option("--force", "Skip confirmation prompt").addHelpText("after", `
43317
+ Examples:
43318
+ $ lua version delete 3 Delete v3 (confirms first)
43319
+ $ lua version delete v3 --force Skip confirmation
43320
+ `).action((version, opts) => versionDeleteCommand(version, opts));
43321
+ const gitGroup = program2.command("git").description("Manage opt-in git auto-commits for this project");
43322
+ gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").action(() => gitConnectCommand());
43323
+ gitGroup.command("disconnect").description("Disable git auto-commits (existing commits/tags untouched)").action(() => gitDisconnectCommand());
43324
+ gitGroup.command("status").description("Show git integration config + last lua-issued commit/tag").action(() => gitStatusCommand());
43325
+ const gitAuthGroup = gitGroup.command("auth").description("Manage git remote provider authentication");
43326
+ gitAuthGroup.command("github").description("Link a GitHub account").option("--device", "Use the device flow (paste a code into github.com/login/device)").option("--force", "Overwrite an existing link without prompting").action((opts) => gitAuthGithubCommand(opts));
43327
+ gitAuthGroup.command("status").description("Show the current git auth status").action(() => gitAuthStatusCommand());
43328
+ gitAuthGroup.command("disconnect [provider]").description("Disconnect a git provider (default: github)").action((provider) => gitAuthDisconnectCommand(provider));
43329
+ program2.command("pull").description("\u{1F4E5} Pull the agent's source code locally").option("--version <version>", "Pull source linked to a specific agent version (requires versioning)").option("--force", "Skip confirmation prompt").addHelpText("after", `
43330
+ Examples:
43331
+ $ lua pull Restore the latest source backup
43332
+ $ lua pull --version 3 Restore the source snapshot from agent version 3
43333
+ $ lua pull --version v3 --force Skip the confirmation prompt
43334
+ `).action((opts) => pullCommand(opts));
41547
43335
  const sourceCmd = program2.command("source").description("\u{1F5C2}\uFE0F Manage workspace source versions");
41548
43336
  sourceCmd.command("list").description("List source versions for the current agent").option("--all", "Show all versions instead of the most recent 50", false).option("--limit <n>", "Number of versions to show", "50").addHelpText("after", `
41549
43337
  Examples: