@swmansion/argent 0.13.1-next.1 → 0.13.1-next.2

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/cli-cmds.mjs CHANGED
@@ -109,8 +109,8 @@ var require_picocolors = __commonJS({
109
109
  var require_src = __commonJS({
110
110
  "../../node_modules/sisteransi/src/index.js"(exports, module) {
111
111
  "use strict";
112
- var ESC2 = "\x1B";
113
- var CSI2 = `${ESC2}[`;
112
+ var ESC3 = "\x1B";
113
+ var CSI2 = `${ESC3}[`;
114
114
  var beep = "\x07";
115
115
  var cursor3 = {
116
116
  to(x, y) {
@@ -134,8 +134,8 @@ var require_src = __commonJS({
134
134
  left: `${CSI2}G`,
135
135
  hide: `${CSI2}?25l`,
136
136
  show: `${CSI2}?25h`,
137
- save: `${ESC2}7`,
138
- restore: `${ESC2}8`
137
+ save: `${ESC3}7`,
138
+ restore: `${ESC3}8`
139
139
  };
140
140
  var scroll = {
141
141
  up: (count = 1) => `${CSI2}S`.repeat(count),
@@ -162,8 +162,8 @@ var require_src = __commonJS({
162
162
  });
163
163
 
164
164
  // ../argent-cli/src/run.ts
165
- import * as fs6 from "node:fs";
166
- import * as path7 from "node:path";
165
+ import * as fs7 from "node:fs";
166
+ import * as path9 from "node:path";
167
167
 
168
168
  // ../argent-tools-client/src/launcher.ts
169
169
  import * as net from "node:net";
@@ -617,8 +617,8 @@ function parseLinkTarget(input) {
617
617
  const host = u3.hostname.startsWith("[") ? u3.hostname.slice(1, -1) : u3.hostname;
618
618
  if (!host) throw new Error(`URL "${input}" is missing a host.`);
619
619
  const port = u3.port ? Number(u3.port) : u3.protocol === "https:" ? 443 : 80;
620
- const path10 = u3.pathname === "/" ? "" : u3.pathname.replace(/\/+$/, "");
621
- const url = `${u3.protocol}//${u3.host}${path10}`;
620
+ const path12 = u3.pathname === "/" ? "" : u3.pathname.replace(/\/+$/, "");
621
+ const url = `${u3.protocol}//${u3.host}${path12}`;
622
622
  const token = u3.username ? decodeURIComponent(u3.username) : void 0;
623
623
  return { url, host, port, ...token ? { token } : {} };
624
624
  }
@@ -930,8 +930,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname2(proce
930
930
  return decodedFile;
931
931
  };
932
932
  }
933
- function normalizeWindowsPath(path10) {
934
- return path10.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
933
+ function normalizeWindowsPath(path12) {
934
+ return path12.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
935
935
  }
936
936
 
937
937
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3483,9 +3483,9 @@ async function addSourceContext(frames) {
3483
3483
  LRU_FILE_CONTENTS_CACHE.reduce();
3484
3484
  return frames;
3485
3485
  }
3486
- function getContextLinesFromFile(path10, ranges, output) {
3486
+ function getContextLinesFromFile(path12, ranges, output) {
3487
3487
  return new Promise((resolve3) => {
3488
- const stream = createReadStream(path10);
3488
+ const stream = createReadStream(path12);
3489
3489
  const lineReaded = createInterface2({
3490
3490
  input: stream
3491
3491
  });
@@ -3500,7 +3500,7 @@ function getContextLinesFromFile(path10, ranges, output) {
3500
3500
  let rangeStart = range[0];
3501
3501
  let rangeEnd = range[1];
3502
3502
  function onStreamError() {
3503
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path10, 1);
3503
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path12, 1);
3504
3504
  lineReaded.close();
3505
3505
  lineReaded.removeAllListeners();
3506
3506
  destroyStreamAndResolve();
@@ -3561,8 +3561,8 @@ function clearLineContext(frame) {
3561
3561
  delete frame.context_line;
3562
3562
  delete frame.post_context;
3563
3563
  }
3564
- function shouldSkipContextLinesForFile(path10) {
3565
- return path10.startsWith("node:") || path10.endsWith(".min.js") || path10.endsWith(".min.cjs") || path10.endsWith(".min.mjs") || path10.startsWith("data:");
3564
+ function shouldSkipContextLinesForFile(path12) {
3565
+ return path12.startsWith("node:") || path12.endsWith(".min.js") || path12.endsWith(".min.cjs") || path12.endsWith(".min.mjs") || path12.startsWith("data:");
3566
3566
  }
3567
3567
  function shouldSkipContextLinesForFrame(frame) {
3568
3568
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -6256,9 +6256,9 @@ function isReplitAgent(env) {
6256
6256
  }
6257
6257
  var DEVIN_MARKER_PATH = "/opt/.devin";
6258
6258
  var JULES_MARKER_PATH = "/opt/environment_summary.sh";
6259
- function safeExists(fileExists, path10) {
6259
+ function safeExists(fileExists, path12) {
6260
6260
  try {
6261
- return fileExists(path10);
6261
+ return fileExists(path12);
6262
6262
  } catch {
6263
6263
  return false;
6264
6264
  }
@@ -6712,108 +6712,137 @@ function getBaseProps(runtime) {
6712
6712
  }
6713
6713
 
6714
6714
  // ../telemetry/src/identity.ts
6715
- import * as crypto from "node:crypto";
6716
- import * as fs2 from "node:fs";
6717
- import * as path5 from "node:path";
6715
+ import * as crypto2 from "node:crypto";
6716
+ import * as fs4 from "node:fs";
6717
+ import * as path8 from "node:path";
6718
6718
 
6719
6719
  // ../telemetry/src/paths.ts
6720
- import * as os from "node:os";
6720
+ import * as path7 from "node:path";
6721
+
6722
+ // ../configuration-core/src/flags.ts
6723
+ import * as fs2 from "node:fs";
6721
6724
  import * as path4 from "node:path";
6722
- function nonEmpty(value) {
6723
- if (value == void 0) return null;
6724
- const trimmed = value.trim();
6725
- return trimmed === "" ? null : value;
6726
- }
6727
- function argentHomeDir() {
6728
- const home = process.platform === "win32" ? nonEmpty(process.env.USERPROFILE) ?? os.homedir() : nonEmpty(process.env.HOME) ?? os.homedir();
6729
- return path4.join(home, ".argent");
6730
- }
6731
- function identityFilePath() {
6732
- return path4.join(argentHomeDir(), "telemetry-id");
6725
+ import { homedir as homedir3 } from "node:os";
6726
+ var FLAG_REGISTRY = [
6727
+ {
6728
+ name: "disable-auto-screenshot",
6729
+ description: "Disable the automatic screenshot captured after interaction tools."
6730
+ },
6731
+ {
6732
+ name: "argent-lens",
6733
+ description: "Argent Lens \u2014 the propose_variant / await_user_selection tools and the Electron preview window for staging UI design variants and letting a human pick among them. Off by default while the feature is in development."
6734
+ },
6735
+ {
6736
+ name: "artifacts-list-endpoint",
6737
+ description: "Expose GET /artifacts for remote artifact inventory consumers."
6738
+ }
6739
+ ];
6740
+ function getFlagDefinition(name, registry = FLAG_REGISTRY) {
6741
+ return registry.find((def) => def.name === name);
6733
6742
  }
6734
- function configFilePath() {
6735
- return path4.join(argentHomeDir(), "config.json");
6743
+ var PROJECT_MARKERS = [".argent", ".git", "package.json"];
6744
+ function resolveProjectRoot(startDir) {
6745
+ const initial = path4.resolve(startDir);
6746
+ let current = initial;
6747
+ while (true) {
6748
+ for (const marker of PROJECT_MARKERS) {
6749
+ if (fs2.existsSync(path4.join(current, marker))) return current;
6750
+ }
6751
+ const parent = path4.dirname(current);
6752
+ if (parent === current) return initial;
6753
+ current = parent;
6754
+ }
6736
6755
  }
6737
- function debugLogPath() {
6738
- return path4.join(argentHomeDir(), "telemetry-debug.log");
6756
+ function getFlagsPath(scope, options = {}) {
6757
+ const home = options.homeDir ?? homedir3();
6758
+ if (scope === "global") {
6759
+ return path4.join(home, ".argent", "flags.json");
6760
+ }
6761
+ const cwd = options.cwd ?? process.cwd();
6762
+ return path4.join(resolveProjectRoot(cwd), ".argent", "flags.json");
6739
6763
  }
6740
-
6741
- // ../telemetry/src/identity.ts
6742
- var cached = null;
6743
- function readOrCreateAnonId() {
6744
- const finalPath = identityFilePath();
6745
- if (cached && cached.path === finalPath) return cached.id;
6746
- const existing = tryReadId(finalPath);
6747
- if (existing) {
6748
- cached = { path: finalPath, id: existing };
6749
- return existing;
6764
+ function readFlagsFile(filePath) {
6765
+ let raw;
6766
+ try {
6767
+ raw = fs2.readFileSync(filePath, "utf8");
6768
+ } catch {
6769
+ return {};
6750
6770
  }
6751
- fs2.mkdirSync(argentHomeDir(), { recursive: true });
6752
- const uuid = crypto.randomUUID();
6753
- for (let attempt = 0; attempt < 3; attempt++) {
6754
- const tmpPath = path5.join(
6755
- argentHomeDir(),
6756
- `.telemetry-id.tmp.${process.pid}.${crypto.randomUUID()}`
6757
- );
6758
- let fd;
6759
- try {
6760
- fd = fs2.openSync(tmpPath, "wx", 384);
6761
- } catch (err) {
6762
- if (err.code === "EEXIST") continue;
6763
- throw err;
6764
- }
6771
+ let parsed;
6772
+ try {
6773
+ parsed = JSON.parse(raw);
6774
+ } catch {
6775
+ return {};
6776
+ }
6777
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
6778
+ const flags2 = parsed.flags;
6779
+ if (!flags2 || typeof flags2 !== "object" || Array.isArray(flags2)) return {};
6780
+ const out = {};
6781
+ for (const [k, v] of Object.entries(flags2)) {
6782
+ if (typeof v === "boolean") out[k] = v;
6783
+ }
6784
+ return out;
6785
+ }
6786
+ function writeFlagsFile(filePath, flags2) {
6787
+ if (Object.keys(flags2).length === 0) {
6788
+ if (fs2.existsSync(filePath)) fs2.rmSync(filePath, { force: true });
6789
+ const parent = path4.dirname(filePath);
6765
6790
  try {
6766
- try {
6767
- fs2.writeSync(fd, uuid);
6768
- fs2.fsyncSync(fd);
6769
- } finally {
6770
- fs2.closeSync(fd);
6771
- }
6772
- fs2.linkSync(tmpPath, finalPath);
6773
- cached = { path: finalPath, id: uuid };
6774
- return uuid;
6775
- } catch (err) {
6776
- if (err.code === "EEXIST") {
6777
- const beatUs = tryReadId(finalPath);
6778
- if (beatUs) {
6779
- cached = { path: finalPath, id: beatUs };
6780
- return beatUs;
6781
- }
6782
- continue;
6783
- }
6784
- throw err;
6785
- } finally {
6786
- try {
6787
- fs2.unlinkSync(tmpPath);
6788
- } catch {
6791
+ if (fs2.existsSync(parent) && fs2.readdirSync(parent).length === 0) {
6792
+ fs2.rmdirSync(parent);
6789
6793
  }
6794
+ } catch {
6790
6795
  }
6796
+ return;
6791
6797
  }
6792
- throw new Error("telemetry: failed to create anonymous identity after retries");
6798
+ fs2.mkdirSync(path4.dirname(filePath), { recursive: true });
6799
+ const tmp = `${filePath}.${process.pid}.tmp`;
6800
+ fs2.writeFileSync(tmp, JSON.stringify({ flags: flags2 }, null, 2) + "\n");
6801
+ fs2.renameSync(tmp, filePath);
6793
6802
  }
6794
- function peekAnonId() {
6795
- return tryReadId(identityFilePath());
6803
+ function readFlags(scope, options = {}) {
6804
+ return readFlagsFile(getFlagsPath(scope, options));
6796
6805
  }
6797
- function tryReadId(filePath) {
6798
- let raw;
6799
- try {
6800
- const stats = fs2.lstatSync(filePath);
6801
- if (!stats.isFile()) return null;
6802
- raw = fs2.readFileSync(filePath, "utf8");
6803
- } catch (err) {
6804
- if (err.code === "ENOENT") return null;
6805
- return null;
6806
- }
6807
- const value = raw.trim();
6808
- if (/^[0-9a-fA-F-]{32,128}$/.test(value)) return value;
6809
- return null;
6806
+ function setFlag(name, value, scope, options = {}) {
6807
+ const filePath = getFlagsPath(scope, options);
6808
+ const current = readFlagsFile(filePath);
6809
+ current[name] = value;
6810
+ writeFlagsFile(filePath, current);
6811
+ }
6812
+ function unsetFlag(name, scope, options = {}) {
6813
+ const filePath = getFlagsPath(scope, options);
6814
+ const current = readFlagsFile(filePath);
6815
+ if (!Object.hasOwn(current, name)) return false;
6816
+ delete current[name];
6817
+ writeFlagsFile(filePath, current);
6818
+ return true;
6819
+ }
6820
+ function isFlagEnabled(name, options = {}) {
6821
+ const projectFlags = readFlags("project", options);
6822
+ if (Object.hasOwn(projectFlags, name)) return projectFlags[name];
6823
+ const globalFlags = readFlags("global", options);
6824
+ if (Object.hasOwn(globalFlags, name)) return globalFlags[name];
6825
+ return false;
6810
6826
  }
6811
6827
 
6812
- // ../telemetry/src/consent.ts
6813
- import * as fs4 from "node:fs";
6828
+ // ../configuration-core/src/paths.ts
6829
+ import * as os from "node:os";
6830
+ import * as path5 from "node:path";
6831
+ function nonEmpty(value) {
6832
+ if (value == void 0) return null;
6833
+ const trimmed = value.trim();
6834
+ return trimmed === "" ? null : value;
6835
+ }
6836
+ function argentHomeDir() {
6837
+ const home = process.platform === "win32" ? nonEmpty(process.env.USERPROFILE) ?? os.homedir() : nonEmpty(process.env.HOME) ?? os.homedir();
6838
+ return path5.join(home, ".argent");
6839
+ }
6840
+ function configFilePath() {
6841
+ return path5.join(argentHomeDir(), "config.json");
6842
+ }
6814
6843
 
6815
- // ../telemetry/src/config-file.ts
6816
- import * as crypto2 from "node:crypto";
6844
+ // ../configuration-core/src/config.ts
6845
+ import * as crypto from "node:crypto";
6817
6846
  import * as fs3 from "node:fs";
6818
6847
  import * as path6 from "node:path";
6819
6848
  function readConfigObject() {
@@ -6876,7 +6905,7 @@ function updateConfig(mutate) {
6876
6905
  const next = readConfigObject();
6877
6906
  mutate(next);
6878
6907
  const finalPath = configFilePath();
6879
- const tmpPath = path6.join(argentHomeDir(), `.config.tmp.${process.pid}.${crypto2.randomUUID()}`);
6908
+ const tmpPath = path6.join(argentHomeDir(), `.config.tmp.${process.pid}.${crypto.randomUUID()}`);
6880
6909
  const fd = fs3.openSync(tmpPath, "wx", 384);
6881
6910
  try {
6882
6911
  fs3.writeSync(fd, JSON.stringify(next, null, 2) + "\n");
@@ -6897,14 +6926,116 @@ function updateConfig(mutate) {
6897
6926
  if (lock) releaseConfigLock(lock);
6898
6927
  }
6899
6928
  }
6929
+ function readLensConfig() {
6930
+ const lens2 = readConfigObject().lens;
6931
+ return lens2 && typeof lens2 === "object" ? lens2 : {};
6932
+ }
6933
+ function getRememberedAgent() {
6934
+ const agent = readLensConfig().agent;
6935
+ return typeof agent === "string" && agent.trim() ? agent : null;
6936
+ }
6937
+ function setRememberedAgent(agentId) {
6938
+ updateConfig((config) => {
6939
+ const lens2 = config.lens && typeof config.lens === "object" ? config.lens : {};
6940
+ lens2.agent = agentId;
6941
+ config.lens = lens2;
6942
+ });
6943
+ }
6944
+ function clearRememberedAgent() {
6945
+ updateConfig((config) => {
6946
+ if (config.lens && typeof config.lens === "object") {
6947
+ delete config.lens.agent;
6948
+ }
6949
+ });
6950
+ }
6951
+
6952
+ // ../telemetry/src/paths.ts
6953
+ function identityFilePath() {
6954
+ return path7.join(argentHomeDir(), "telemetry-id");
6955
+ }
6956
+ function debugLogPath() {
6957
+ return path7.join(argentHomeDir(), "telemetry-debug.log");
6958
+ }
6959
+
6960
+ // ../telemetry/src/identity.ts
6961
+ var cached = null;
6962
+ function readOrCreateAnonId() {
6963
+ const finalPath = identityFilePath();
6964
+ if (cached && cached.path === finalPath) return cached.id;
6965
+ const existing = tryReadId(finalPath);
6966
+ if (existing) {
6967
+ cached = { path: finalPath, id: existing };
6968
+ return existing;
6969
+ }
6970
+ fs4.mkdirSync(argentHomeDir(), { recursive: true });
6971
+ const uuid = crypto2.randomUUID();
6972
+ for (let attempt = 0; attempt < 3; attempt++) {
6973
+ const tmpPath = path8.join(
6974
+ argentHomeDir(),
6975
+ `.telemetry-id.tmp.${process.pid}.${crypto2.randomUUID()}`
6976
+ );
6977
+ let fd;
6978
+ try {
6979
+ fd = fs4.openSync(tmpPath, "wx", 384);
6980
+ } catch (err) {
6981
+ if (err.code === "EEXIST") continue;
6982
+ throw err;
6983
+ }
6984
+ try {
6985
+ try {
6986
+ fs4.writeSync(fd, uuid);
6987
+ fs4.fsyncSync(fd);
6988
+ } finally {
6989
+ fs4.closeSync(fd);
6990
+ }
6991
+ fs4.linkSync(tmpPath, finalPath);
6992
+ cached = { path: finalPath, id: uuid };
6993
+ return uuid;
6994
+ } catch (err) {
6995
+ if (err.code === "EEXIST") {
6996
+ const beatUs = tryReadId(finalPath);
6997
+ if (beatUs) {
6998
+ cached = { path: finalPath, id: beatUs };
6999
+ return beatUs;
7000
+ }
7001
+ continue;
7002
+ }
7003
+ throw err;
7004
+ } finally {
7005
+ try {
7006
+ fs4.unlinkSync(tmpPath);
7007
+ } catch {
7008
+ }
7009
+ }
7010
+ }
7011
+ throw new Error("telemetry: failed to create anonymous identity after retries");
7012
+ }
7013
+ function peekAnonId() {
7014
+ return tryReadId(identityFilePath());
7015
+ }
7016
+ function tryReadId(filePath) {
7017
+ let raw;
7018
+ try {
7019
+ const stats = fs4.lstatSync(filePath);
7020
+ if (!stats.isFile()) return null;
7021
+ raw = fs4.readFileSync(filePath, "utf8");
7022
+ } catch (err) {
7023
+ if (err.code === "ENOENT") return null;
7024
+ return null;
7025
+ }
7026
+ const value = raw.trim();
7027
+ if (/^[0-9a-fA-F-]{32,128}$/.test(value)) return value;
7028
+ return null;
7029
+ }
6900
7030
 
6901
7031
  // ../telemetry/src/consent.ts
7032
+ import * as fs5 from "node:fs";
6902
7033
  var cache = { current: null };
6903
7034
  var sessionOverride = null;
6904
7035
  function readConfigOverride() {
6905
7036
  let stats;
6906
7037
  try {
6907
- stats = fs4.lstatSync(configFilePath());
7038
+ stats = fs5.lstatSync(configFilePath());
6908
7039
  } catch (err) {
6909
7040
  if (err.code === "ENOENT") {
6910
7041
  cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
@@ -6924,7 +7055,7 @@ function readConfigOverride() {
6924
7055
  }
6925
7056
  let parsedEnabled = null;
6926
7057
  try {
6927
- const raw = fs4.readFileSync(configFilePath(), "utf8");
7058
+ const raw = fs5.readFileSync(configFilePath(), "utf8");
6928
7059
  const json = JSON.parse(raw);
6929
7060
  if (json && typeof json === "object") {
6930
7061
  const t2 = json.telemetry;
@@ -6985,7 +7116,7 @@ function writeConsentFlag(enabled) {
6985
7116
  }
6986
7117
 
6987
7118
  // ../telemetry/src/debug.ts
6988
- import * as fs5 from "node:fs";
7119
+ import * as fs6 from "node:fs";
6989
7120
  function isDebugEnabled(env = process.env) {
6990
7121
  const v = env.ARGENT_TELEMETRY_DEBUG;
6991
7122
  if (!v) return false;
@@ -7012,8 +7143,8 @@ function emitDebugPayload(payload) {
7012
7143
  } catch {
7013
7144
  }
7014
7145
  try {
7015
- fs5.mkdirSync(argentHomeDir(), { recursive: true });
7016
- fs5.appendFileSync(debugLogPath(), line + "\n");
7146
+ fs6.mkdirSync(argentHomeDir(), { recursive: true });
7147
+ fs6.appendFileSync(debugLogPath(), line + "\n");
7017
7148
  } catch {
7018
7149
  }
7019
7150
  }
@@ -7382,8 +7513,8 @@ async function fetchImageToFile(result, outPath) {
7382
7513
  const res = await fetch(url);
7383
7514
  if (!res.ok) throw new Error(`Failed to download image: ${res.status} ${res.statusText}`);
7384
7515
  const buf = Buffer.from(await res.arrayBuffer());
7385
- fs6.mkdirSync(path7.dirname(path7.resolve(outPath)), { recursive: true });
7386
- fs6.writeFileSync(outPath, buf);
7516
+ fs7.mkdirSync(path9.dirname(path9.resolve(outPath)), { recursive: true });
7517
+ fs7.writeFileSync(outPath, buf);
7387
7518
  }
7388
7519
  function renderResult(result, outputHint, images, json) {
7389
7520
  if (json) return JSON.stringify(result, null, 2);
@@ -7522,8 +7653,8 @@ Examples:
7522
7653
  if (outPath && meta.outputHint === "image") {
7523
7654
  try {
7524
7655
  if (images.length > 0) {
7525
- fs6.mkdirSync(path7.dirname(path7.resolve(outPath)), { recursive: true });
7526
- fs6.writeFileSync(outPath, images[0].data);
7656
+ fs7.mkdirSync(path9.dirname(path9.resolve(outPath)), { recursive: true });
7657
+ fs7.writeFileSync(outPath, images[0].data);
7527
7658
  } else if (result && typeof result === "object") {
7528
7659
  await fetchImageToFile(result, outPath);
7529
7660
  }
@@ -7623,16 +7754,16 @@ Options:
7623
7754
  }
7624
7755
 
7625
7756
  // ../argent-cli/src/server.ts
7626
- import * as fs7 from "node:fs";
7627
- import * as path8 from "node:path";
7628
- import { homedir as homedir4, networkInterfaces } from "node:os";
7757
+ import * as fs8 from "node:fs";
7758
+ import * as path10 from "node:path";
7759
+ import { homedir as homedir5, networkInterfaces } from "node:os";
7629
7760
  import { spawn as spawn2 } from "node:child_process";
7630
- var STATE_DIR2 = path8.join(homedir4(), ".argent");
7631
- var STATE_FILE2 = path8.join(STATE_DIR2, "tool-server.json");
7632
- var LOG_FILE2 = path8.join(STATE_DIR2, "tool-server.log");
7761
+ var STATE_DIR2 = path10.join(homedir5(), ".argent");
7762
+ var STATE_FILE2 = path10.join(STATE_DIR2, "tool-server.json");
7763
+ var LOG_FILE2 = path10.join(STATE_DIR2, "tool-server.log");
7633
7764
  function readState2() {
7634
7765
  try {
7635
- return JSON.parse(fs7.readFileSync(STATE_FILE2, "utf8"));
7766
+ return JSON.parse(fs8.readFileSync(STATE_FILE2, "utf8"));
7636
7767
  } catch {
7637
7768
  return null;
7638
7769
  }
@@ -7682,12 +7813,12 @@ async function stopCmd() {
7682
7813
  console.log(`tool-server stopped (pid ${state2.pid}).`);
7683
7814
  }
7684
7815
  function logsCmd(follow) {
7685
- if (!fs7.existsSync(LOG_FILE2)) {
7816
+ if (!fs8.existsSync(LOG_FILE2)) {
7686
7817
  console.log(`No log file at ${LOG_FILE2}`);
7687
7818
  return;
7688
7819
  }
7689
7820
  if (!follow) {
7690
- process.stdout.write(fs7.readFileSync(LOG_FILE2, "utf8"));
7821
+ process.stdout.write(fs8.readFileSync(LOG_FILE2, "utf8"));
7691
7822
  return;
7692
7823
  }
7693
7824
  const child = spawn2("tail", ["-f", LOG_FILE2], { stdio: "inherit" });
@@ -8029,116 +8160,940 @@ async function server(argv, options) {
8029
8160
  }
8030
8161
  }
8031
8162
 
8032
- // ../argent-cli/src/flags.ts
8033
- var import_picocolors = __toESM(require_picocolors(), 1);
8163
+ // ../argent-cli/src/lens.ts
8164
+ import * as fs9 from "node:fs";
8165
+ import * as os2 from "node:os";
8166
+ import * as path11 from "node:path";
8034
8167
 
8035
- // ../configuration-core/src/flags.ts
8036
- import * as fs8 from "node:fs";
8037
- import * as path9 from "node:path";
8038
- import { homedir as homedir5 } from "node:os";
8039
- var FLAG_REGISTRY = [
8168
+ // ../argent-cli/src/lens-terminal.ts
8169
+ import { execFileSync as execFileSync2 } from "node:child_process";
8170
+ import { existsSync as existsSync5 } from "node:fs";
8171
+ var ITERM_PATHS = ["/Applications/iTerm.app", `${process.env.HOME ?? ""}/Applications/iTerm.app`];
8172
+ function isITermInstalled(existsSync7 = defaultExists) {
8173
+ return ITERM_PATHS.some((p) => p && existsSync7(p));
8174
+ }
8175
+ function defaultExists(p) {
8176
+ return existsSync5(p);
8177
+ }
8178
+ function resolveTerminal(preferred = "iterm", existsSync7 = defaultExists) {
8179
+ if (preferred === "iterm" && isITermInstalled(existsSync7)) return "iterm";
8180
+ return "terminal";
8181
+ }
8182
+ function escapeAppleScript(s) {
8183
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
8184
+ }
8185
+ function shellQuote(s) {
8186
+ return "'" + s.replace(/'/g, "'\\''") + "'";
8187
+ }
8188
+ function flattenLine(s) {
8189
+ return s.replace(/\s*[\r\n]+\s*/g, " ").trim();
8190
+ }
8191
+ function buildSpawnScript(app, shellCommand) {
8192
+ const esc = escapeAppleScript(shellCommand);
8193
+ if (app === "iterm") {
8194
+ return [
8195
+ 'tell application "iTerm"',
8196
+ " activate",
8197
+ " set w to (create window with default profile)",
8198
+ ' set _sid to ""',
8199
+ ' set _tty to ""',
8200
+ " tell current session of w",
8201
+ ` write text "${esc}"`,
8202
+ " set _tty to tty",
8203
+ " set _sid to id",
8204
+ " end tell",
8205
+ " set _wid to (id of w) as string",
8206
+ "end tell",
8207
+ 'return _wid & "|" & _sid & "|" & _tty'
8208
+ ].join("\n");
8209
+ }
8210
+ return [
8211
+ 'tell application "Terminal"',
8212
+ " activate",
8213
+ ` set _tab to do script "${esc}"`,
8214
+ " set _tty to tty of _tab",
8215
+ " set _wid to (id of front window) as string",
8216
+ "end tell",
8217
+ 'return _wid & "||" & _tty'
8218
+ ].join("\n");
8219
+ }
8220
+ function buildWriteScript(session, text2) {
8221
+ const esc = escapeAppleScript(flattenLine(text2));
8222
+ if (session.app === "iterm") {
8223
+ return [
8224
+ 'tell application "iTerm"',
8225
+ " set _found to false",
8226
+ " repeat with w in windows",
8227
+ " repeat with t in tabs of w",
8228
+ " repeat with s in sessions of t",
8229
+ ` if (id of s) is "${session.sessionId}" then`,
8230
+ " tell s to write text (character id 27) newline no",
8231
+ " delay 0.15",
8232
+ ` tell s to write text "${esc}"`,
8233
+ " delay 0.2",
8234
+ ' tell s to write text ""',
8235
+ " set _found to true",
8236
+ " end if",
8237
+ " end repeat",
8238
+ " end repeat",
8239
+ " end repeat",
8240
+ ' if not _found then error "session gone"',
8241
+ "end tell"
8242
+ ].join("\n");
8243
+ }
8244
+ return [
8245
+ 'tell application "Terminal"',
8246
+ " set _found to false",
8247
+ " repeat with w in windows",
8248
+ ` if (id of w as string) is "${session.windowId}" then`,
8249
+ " do script (character id 27) in (selected tab of w)",
8250
+ " delay 0.15",
8251
+ ` do script "${esc}" in (selected tab of w)`,
8252
+ " delay 0.2",
8253
+ ' do script "" in (selected tab of w)',
8254
+ " set _found to true",
8255
+ " end if",
8256
+ " end repeat",
8257
+ ' if not _found then error "window gone"',
8258
+ "end tell"
8259
+ ].join("\n");
8260
+ }
8261
+ function buildReadScript(session) {
8262
+ if (session.app === "iterm") {
8263
+ return [
8264
+ 'tell application "iTerm"',
8265
+ " repeat with w in windows",
8266
+ " repeat with t in tabs of w",
8267
+ " repeat with s in sessions of t",
8268
+ ` if (id of s) is "${session.sessionId}" then return (text of s)`,
8269
+ " end repeat",
8270
+ " end repeat",
8271
+ " end repeat",
8272
+ ' return ""',
8273
+ "end tell"
8274
+ ].join("\n");
8275
+ }
8276
+ return [
8277
+ 'tell application "Terminal"',
8278
+ " repeat with w in windows",
8279
+ ` if (id of w as string) is "${session.windowId}" then return ((contents of selected tab of w) as text)`,
8280
+ " end repeat",
8281
+ ' return ""',
8282
+ "end tell"
8283
+ ].join("\n");
8284
+ }
8285
+ function buildEnterScript(session) {
8286
+ if (session.app === "iterm") {
8287
+ return [
8288
+ 'tell application "iTerm"',
8289
+ " repeat with w in windows",
8290
+ " repeat with t in tabs of w",
8291
+ " repeat with s in sessions of t",
8292
+ ` if (id of s) is "${session.sessionId}" then tell s to write text ""`,
8293
+ " end repeat",
8294
+ " end repeat",
8295
+ " end repeat",
8296
+ "end tell"
8297
+ ].join("\n");
8298
+ }
8299
+ return [
8300
+ 'tell application "Terminal"',
8301
+ " repeat with w in windows",
8302
+ ` if (id of w as string) is "${session.windowId}" then do script "" in (selected tab of w)`,
8303
+ " end repeat",
8304
+ "end tell"
8305
+ ].join("\n");
8306
+ }
8307
+ function parseCapture(out) {
8308
+ const parts = out.trim().split("|");
8309
+ return {
8310
+ windowId: parts[0]?.trim() ?? "",
8311
+ sessionId: parts[1]?.trim() ?? "",
8312
+ tty: parts[2]?.trim() ?? ""
8313
+ };
8314
+ }
8315
+ function shortTty(tty) {
8316
+ return tty.startsWith("/dev/") ? tty.slice(5) : tty;
8317
+ }
8318
+ function parseAliveTtys(psOutput) {
8319
+ const set = /* @__PURE__ */ new Set();
8320
+ for (const line of psOutput.split("\n")) {
8321
+ const t2 = line.trim();
8322
+ if (t2 && t2 !== "??") set.add(t2);
8323
+ }
8324
+ return set;
8325
+ }
8326
+ function runOsascript(script) {
8327
+ try {
8328
+ return execFileSync2("/usr/bin/osascript", ["-e", script], {
8329
+ encoding: "utf8",
8330
+ stdio: ["ignore", "pipe", "pipe"]
8331
+ });
8332
+ } catch (err) {
8333
+ const e = err;
8334
+ const stderr = (typeof e.stderr === "string" ? e.stderr : e.stderr?.toString()) ?? "";
8335
+ throw new Error(`osascript failed: ${stderr.trim() || e.message || "unknown error"}`, {
8336
+ cause: err
8337
+ });
8338
+ }
8339
+ }
8340
+ function spawnTerminalSession(command, app) {
8341
+ const out = runOsascript(buildSpawnScript(app, command));
8342
+ const { windowId, sessionId: sessionId2, tty } = parseCapture(out);
8343
+ return { app, windowId, sessionId: sessionId2, tty };
8344
+ }
8345
+ function writeToSession(session, text2) {
8346
+ try {
8347
+ runOsascript(buildWriteScript(session, text2));
8348
+ return true;
8349
+ } catch {
8350
+ return false;
8351
+ }
8352
+ }
8353
+ function readSessionText(session) {
8354
+ try {
8355
+ return runOsascript(buildReadScript(session));
8356
+ } catch {
8357
+ return null;
8358
+ }
8359
+ }
8360
+ function pressEnter(session) {
8361
+ try {
8362
+ runOsascript(buildEnterScript(session));
8363
+ return true;
8364
+ } catch {
8365
+ return false;
8366
+ }
8367
+ }
8368
+ function aliveTtys() {
8369
+ try {
8370
+ const out = execFileSync2("/bin/ps", ["-A", "-o", "tty="], { encoding: "utf8" });
8371
+ return parseAliveTtys(out);
8372
+ } catch {
8373
+ return /* @__PURE__ */ new Set();
8374
+ }
8375
+ }
8376
+ function isSessionAlive(session) {
8377
+ if (!session.tty) return false;
8378
+ return aliveTtys().has(shortTty(session.tty));
8379
+ }
8380
+
8381
+ // ../argent-cli/src/lens-pty.ts
8382
+ import { createRequire } from "node:module";
8383
+ var nodeRequire = createRequire(import.meta.url);
8384
+ function loadNodePty(req = nodeRequire) {
8385
+ try {
8386
+ const mod = req("node-pty");
8387
+ return mod && typeof mod.spawn === "function" ? mod : null;
8388
+ } catch {
8389
+ return null;
8390
+ }
8391
+ }
8392
+ var ESC = "\x1B";
8393
+ var ENTER = "\r";
8394
+ var BEAT_AFTER_ESC_MS = 150;
8395
+ var BEAT_AFTER_TEXT_MS = 200;
8396
+ var QUIET_BEFORE_INJECT_MS = 600;
8397
+ var MAX_QUIET_WAIT_MS = 3e3;
8398
+ var QUIET_POLL_MS = 100;
8399
+ function ptyInjectBeats(text2) {
8400
+ return [
8401
+ { delayBeforeMs: 0, data: ESC },
8402
+ { delayBeforeMs: BEAT_AFTER_ESC_MS, data: flattenLine(text2) },
8403
+ { delayBeforeMs: BEAT_AFTER_TEXT_MS, data: ENTER }
8404
+ ];
8405
+ }
8406
+ function sleep(ms) {
8407
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
8408
+ }
8409
+ var DEFAULT_COLS = 80;
8410
+ var DEFAULT_ROWS = 24;
8411
+ function startPtyProxy(opts) {
8412
+ const stdin2 = opts.stdin ?? process.stdin;
8413
+ const stdout2 = opts.stdout ?? process.stdout;
8414
+ const signals = opts.signals ?? process;
8415
+ const cols = stdout2.columns ?? DEFAULT_COLS;
8416
+ const rows = stdout2.rows ?? DEFAULT_ROWS;
8417
+ const term = opts.pty.spawn("/bin/sh", ["-c", opts.command], {
8418
+ name: process.env.TERM || "xterm-256color",
8419
+ cols,
8420
+ rows,
8421
+ cwd: opts.cwd,
8422
+ env: opts.env ?? process.env
8423
+ });
8424
+ const observers = [];
8425
+ const exitCbs = [];
8426
+ const dataSub = term.onData((d) => {
8427
+ stdout2.write(d);
8428
+ for (const cb of observers) cb(d);
8429
+ });
8430
+ const wasRaw = Boolean(stdin2.isRaw);
8431
+ if (stdin2.isTTY && stdin2.setRawMode) stdin2.setRawMode(true);
8432
+ stdin2.resume();
8433
+ let lastUserInputAt = 0;
8434
+ const onStdin = (d) => {
8435
+ lastUserInputAt = Date.now();
8436
+ term.write(d.toString("utf8"));
8437
+ };
8438
+ stdin2.on("data", onStdin);
8439
+ const onResize = () => {
8440
+ if (stdout2.columns && stdout2.rows) term.resize(stdout2.columns, stdout2.rows);
8441
+ };
8442
+ signals.on("SIGWINCH", onResize);
8443
+ let disposed = false;
8444
+ const cleanup = () => {
8445
+ if (disposed) return;
8446
+ disposed = true;
8447
+ try {
8448
+ dataSub.dispose();
8449
+ } catch {
8450
+ }
8451
+ stdin2.off("data", onStdin);
8452
+ signals.off("SIGWINCH", onResize);
8453
+ if (stdin2.isTTY && stdin2.setRawMode) stdin2.setRawMode(wasRaw);
8454
+ stdin2.pause();
8455
+ };
8456
+ term.onExit(({ exitCode }) => {
8457
+ cleanup();
8458
+ for (const cb of exitCbs) cb(exitCode ?? 0);
8459
+ });
8460
+ const waitForTypingPause = async () => {
8461
+ const started = Date.now();
8462
+ while (!disposed && lastUserInputAt > 0 && Date.now() - lastUserInputAt < QUIET_BEFORE_INJECT_MS && Date.now() - started < MAX_QUIET_WAIT_MS) {
8463
+ await sleep(QUIET_POLL_MS);
8464
+ }
8465
+ };
8466
+ let queue = Promise.resolve();
8467
+ return {
8468
+ pid: term.pid,
8469
+ inject(text2) {
8470
+ if (disposed) return false;
8471
+ const beats = ptyInjectBeats(text2);
8472
+ queue = queue.then(async () => {
8473
+ await waitForTypingPause();
8474
+ for (const beat of beats) {
8475
+ if (beat.delayBeforeMs) await sleep(beat.delayBeforeMs);
8476
+ if (disposed) return;
8477
+ term.write(beat.data);
8478
+ }
8479
+ }).catch(() => {
8480
+ });
8481
+ return true;
8482
+ },
8483
+ write(data) {
8484
+ if (disposed) return false;
8485
+ try {
8486
+ term.write(data);
8487
+ return true;
8488
+ } catch {
8489
+ return false;
8490
+ }
8491
+ },
8492
+ onData(cb) {
8493
+ observers.push(cb);
8494
+ },
8495
+ onExit(cb) {
8496
+ exitCbs.push(cb);
8497
+ },
8498
+ dispose() {
8499
+ const wasDisposed = disposed;
8500
+ cleanup();
8501
+ if (!wasDisposed) {
8502
+ try {
8503
+ term.kill();
8504
+ } catch {
8505
+ }
8506
+ }
8507
+ }
8508
+ };
8509
+ }
8510
+
8511
+ // ../argent-cli/src/lens-stream.ts
8512
+ function parseSseBuffer(buffer) {
8513
+ const normalised = buffer.replace(/\r\n/g, "\n");
8514
+ const events = [];
8515
+ let rest = normalised;
8516
+ for (; ; ) {
8517
+ const sep3 = rest.indexOf("\n\n");
8518
+ if (sep3 === -1) break;
8519
+ const rawFrame = rest.slice(0, sep3);
8520
+ rest = rest.slice(sep3 + 2);
8521
+ let event = "message";
8522
+ const dataLines = [];
8523
+ for (const line of rawFrame.split("\n")) {
8524
+ if (line === "" || line.startsWith(":")) continue;
8525
+ const colon = line.indexOf(":");
8526
+ const field = colon === -1 ? line : line.slice(0, colon);
8527
+ let value = colon === -1 ? "" : line.slice(colon + 1);
8528
+ if (value.startsWith(" ")) value = value.slice(1);
8529
+ if (field === "event") event = value;
8530
+ else if (field === "data") dataLines.push(value);
8531
+ }
8532
+ if (dataLines.length > 0) events.push({ event, data: dataLines.join("\n") });
8533
+ }
8534
+ return { events, rest };
8535
+ }
8536
+ async function* lensEvents(baseUrl, signal) {
8537
+ const res = await fetch(`${baseUrl}/preview/lens-stream`, {
8538
+ signal,
8539
+ headers: { Accept: "text/event-stream" }
8540
+ });
8541
+ if (!res.ok || !res.body) {
8542
+ throw new Error(`lens-stream: HTTP ${res.status}`);
8543
+ }
8544
+ const reader = res.body.getReader();
8545
+ const decoder = new TextDecoder();
8546
+ let buf = "";
8547
+ try {
8548
+ for (; ; ) {
8549
+ const { value, done } = await reader.read();
8550
+ if (done) return;
8551
+ buf += decoder.decode(value, { stream: true });
8552
+ const { events, rest } = parseSseBuffer(buf);
8553
+ buf = rest;
8554
+ for (const ev of events) yield ev;
8555
+ }
8556
+ } finally {
8557
+ try {
8558
+ reader.releaseLock();
8559
+ } catch {
8560
+ }
8561
+ }
8562
+ }
8563
+
8564
+ // ../argent-cli/src/lens-agents.ts
8565
+ import { execFileSync as execFileSync3 } from "node:child_process";
8566
+ var AGENTS = [
8040
8567
  {
8041
- name: "disable-auto-screenshot",
8042
- description: "Disable the automatic screenshot captured after interaction tools."
8568
+ id: "claude",
8569
+ displayName: "Claude Code",
8570
+ bin: "claude",
8571
+ launch: (cwd, seed) => `cd ${cwd} 2>/dev/null; claude "$(cat ${seed})"`
8043
8572
  },
8044
8573
  {
8045
- name: "argent-lens",
8046
- description: "Argent Lens \u2014 the propose_variant / await_user_selection tools and the Electron preview window for staging UI design variants and letting a human pick among them. Off by default while the feature is in development."
8574
+ id: "codex",
8575
+ displayName: "Codex CLI",
8576
+ bin: "codex",
8577
+ launch: (cwd, seed) => `cd ${cwd} 2>/dev/null; codex "$(cat ${seed})"`
8047
8578
  },
8048
8579
  {
8049
- name: "artifacts-list-endpoint",
8050
- description: "Expose GET /artifacts for remote artifact inventory consumers."
8580
+ id: "gemini",
8581
+ displayName: "Gemini CLI",
8582
+ bin: "gemini",
8583
+ launch: (cwd, seed) => `cd ${cwd} 2>/dev/null; gemini -i "$(cat ${seed})"`
8584
+ },
8585
+ {
8586
+ id: "opencode",
8587
+ displayName: "OpenCode",
8588
+ bin: "opencode",
8589
+ launch: (cwd) => `cd ${cwd} 2>/dev/null; opencode`,
8590
+ injectSeed: true
8591
+ },
8592
+ {
8593
+ id: "cursor",
8594
+ displayName: "Cursor CLI",
8595
+ bin: "cursor-agent",
8596
+ launch: (cwd, seed) => `cd ${cwd} 2>/dev/null; cursor-agent "$(cat ${seed})"`
8051
8597
  }
8052
8598
  ];
8053
- function getFlagDefinition(name, registry = FLAG_REGISTRY) {
8054
- return registry.find((def) => def.name === name);
8599
+ function findAgentById(id) {
8600
+ return AGENTS.find((a2) => a2.id === id);
8055
8601
  }
8056
- var PROJECT_MARKERS = [".argent", ".git", "package.json"];
8057
- function resolveProjectRoot(startDir) {
8058
- const initial = path9.resolve(startDir);
8059
- let current = initial;
8060
- while (true) {
8061
- for (const marker of PROJECT_MARKERS) {
8062
- if (fs8.existsSync(path9.join(current, marker))) return current;
8602
+ function agentIds() {
8603
+ return AGENTS.map((a2) => a2.id);
8604
+ }
8605
+ function defaultIsOnPath(bin) {
8606
+ try {
8607
+ execFileSync3(process.platform === "win32" ? "where" : "which", [bin], {
8608
+ stdio: ["ignore", "ignore", "ignore"]
8609
+ });
8610
+ return true;
8611
+ } catch {
8612
+ return false;
8613
+ }
8614
+ }
8615
+ function isAgentInstalled(agent, isOnPath = defaultIsOnPath) {
8616
+ return isOnPath(agent.bin);
8617
+ }
8618
+ function detectInstalledAgents(isOnPath = defaultIsOnPath) {
8619
+ return AGENTS.filter((a2) => isOnPath(a2.bin));
8620
+ }
8621
+
8622
+ // ../argent-cli/src/lens.ts
8623
+ var LIVENESS_POLL_MS = 1200;
8624
+ var SPAWN_GRACE_MS = 8e3;
8625
+ var DEATH_CONFIRMATIONS = 3;
8626
+ var SSE_RECONNECT_MS = 1e3;
8627
+ function sleep2(ms) {
8628
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
8629
+ }
8630
+ var TRUST_PROMPT_RE = /trust this folder|do you trust|yes,? i trust|trust the files in this/i;
8631
+ async function dismissTrustPrompt(session) {
8632
+ const deadline = Date.now() + 12e3;
8633
+ while (Date.now() < deadline) {
8634
+ await sleep2(700);
8635
+ const text2 = readSessionText(session);
8636
+ if (text2 == null) {
8637
+ pressEnter(session);
8638
+ return;
8639
+ }
8640
+ if (TRUST_PROMPT_RE.test(text2)) {
8641
+ pressEnter(session);
8642
+ return;
8063
8643
  }
8064
- const parent = path9.dirname(current);
8065
- if (parent === current) return initial;
8066
- current = parent;
8067
8644
  }
8068
8645
  }
8069
- function getFlagsPath(scope, options = {}) {
8070
- const home = options.homeDir ?? homedir5();
8071
- if (scope === "global") {
8072
- return path9.join(home, ".argent", "flags.json");
8646
+ function dismissTrustPromptViaPty(proxy) {
8647
+ let acc = "";
8648
+ let done = false;
8649
+ const deadline = Date.now() + 12e3;
8650
+ proxy.onData((chunk) => {
8651
+ if (done) return;
8652
+ if (Date.now() > deadline) {
8653
+ done = true;
8654
+ return;
8655
+ }
8656
+ acc += chunk;
8657
+ if (TRUST_PROMPT_RE.test(acc)) {
8658
+ done = true;
8659
+ proxy.write("\r");
8660
+ }
8661
+ });
8662
+ }
8663
+ async function injectSeedViaPty(proxy) {
8664
+ await sleep2(5e3);
8665
+ proxy.inject(buildSeedPrompt());
8666
+ }
8667
+ function isInteractiveTty() {
8668
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
8669
+ }
8670
+ var errMsg = (e) => e instanceof Error ? e.message : String(e);
8671
+ function planAgent(agentId) {
8672
+ if (agentId) {
8673
+ const want = findAgentById(agentId);
8674
+ if (!want) {
8675
+ process.stderr.write(
8676
+ `lens: unknown agent "${agentId}". Choose one of: ${agentIds().join(", ")}
8677
+ `
8678
+ );
8679
+ process.exit(2);
8680
+ }
8681
+ if (!isAgentInstalled(want)) {
8682
+ process.stderr.write(
8683
+ `lens: ${want.displayName} (${want.bin}) is not installed or not on PATH.
8684
+ `
8685
+ );
8686
+ process.exit(1);
8687
+ }
8688
+ return { agent: want };
8073
8689
  }
8074
- const cwd = options.cwd ?? process.cwd();
8075
- return path9.join(resolveProjectRoot(cwd), ".argent", "flags.json");
8690
+ const installed = detectInstalledAgents();
8691
+ if (installed.length === 0) {
8692
+ process.stderr.write(
8693
+ `lens: no supported coding-agent CLI found on PATH.
8694
+ Install one of: ${AGENTS.map((a2) => a2.bin).join(", ")}, then re-run argent lens.
8695
+ `
8696
+ );
8697
+ process.exit(1);
8698
+ }
8699
+ const remembered = getRememberedAgent();
8700
+ if (remembered) {
8701
+ const found = installed.find((a2) => a2.id === remembered);
8702
+ if (found) return { agent: found };
8703
+ }
8704
+ if (installed.length === 1) return { agent: installed[0] };
8705
+ return { choose: installed };
8076
8706
  }
8077
- function readFlagsFile(filePath) {
8078
- let raw;
8707
+ async function injectSeedAfterBoot(session) {
8708
+ await sleep2(5e3);
8709
+ writeToSession(session, buildSeedPrompt());
8710
+ }
8711
+ function buildSeedPrompt() {
8712
+ return [
8713
+ "You are running inside an Argent Lens CLI session. The user has the Argent Lens",
8714
+ "preview window open and bound to THIS terminal.",
8715
+ "",
8716
+ "Match your response to the request. If it has a single obvious outcome \u2014 moving or",
8717
+ "renaming something, tweaking one value, or anything with no meaningful design",
8718
+ "alternatives \u2014 just make the change directly and report what you did; do not",
8719
+ 'manufacture variants. Only when the request is genuinely open-ended ("restyle",',
8720
+ '"redesign", "make it nicer", or anything with a real design space) should you stage',
8721
+ "variants with the `propose_variant` Argent tool \u2014 at least two per element so the",
8722
+ "user has a real choice.",
8723
+ "",
8724
+ "Either way, end your turn when you're done \u2014 you never block waiting for a pick in",
8725
+ "this session (feedback comes back to you as a message).",
8726
+ "",
8727
+ "The user reviews any staged variants in the Lens window; their feedback arrives here",
8728
+ 'as a normal message prefixed "[Argent Lens]" (the variants they chose, comments, and',
8729
+ "change requests). Act on it the same way \u2014 direct edits for concrete fixes, fresh",
8730
+ "variants only where the design is still open.",
8731
+ "",
8732
+ "Wait for the user's first instruction."
8733
+ ].join(" ");
8734
+ }
8735
+ function formatLensFeedback(o2) {
8736
+ const parts = [];
8737
+ const sel = (m) => `[${m.by}=${m.value}]`;
8738
+ const chosen = o2.selections.filter((s) => s.chosenVariant);
8739
+ if (chosen.length) {
8740
+ parts.push(
8741
+ "Chosen variants \u2014 " + chosen.map((s) => {
8742
+ const v = s.chosenVariant;
8743
+ const summary = v.summary ? ` (${v.summary})` : "";
8744
+ const where = v.filePath ? ` [src: ${v.filePath}]` : "";
8745
+ const note = s.comment ? ` (note: ${s.comment})` : "";
8746
+ return `"${s.element}" ${sel(s.match)} \u2192 "${v.name}"${summary}${where}${note}`;
8747
+ }).join("; ")
8748
+ );
8749
+ }
8750
+ const notedNoPick = o2.selections.filter((s) => !s.chosenVariant && s.comment);
8751
+ if (notedNoPick.length) {
8752
+ parts.push(
8753
+ "Element notes \u2014 " + notedNoPick.map((s) => `"${s.element}" ${sel(s.match)}: ${s.comment}`).join("; ")
8754
+ );
8755
+ }
8756
+ const touched = new Set([...chosen, ...notedNoPick].map((s) => s.element));
8757
+ const leftAsIs = o2.unselected.map((u3) => u3.element).filter((e) => !touched.has(e));
8758
+ if (leftAsIs.length) {
8759
+ parts.push("Left as-is (reviewed, no change) \u2014 " + leftAsIs.map((e) => `"${e}"`).join(", "));
8760
+ }
8761
+ if (o2.annotations.length) {
8762
+ parts.push(
8763
+ "Comments on the screen \u2014 " + o2.annotations.map((a2) => `"${a2.target}" ${sel(a2.match)}: ${a2.comment}`).join("; ")
8764
+ );
8765
+ }
8766
+ if (o2.globalComment) parts.push("Overall direction \u2014 " + o2.globalComment);
8767
+ const body = parts.length ? parts.join(". ") : "No specific picks were made; review the current variants in the preview window.";
8768
+ const chosenWithComment = chosen.filter((s) => s.comment);
8769
+ const hasDirection = chosenWithComment.length > 0 || notedNoPick.length > 0 || o2.annotations.length > 0 || Boolean(o2.globalComment);
8770
+ const applyChosen = chosen.length ? "Apply the chosen variants to their source files where given. " : "";
8771
+ const closing = hasDirection ? "Handle each request with the smallest change that satisfies it: for a single obvious outcome (a move, rename, or one-value tweak) just make the edit and report it; stage fresh propose_variant options (at least two per element) only where the direction is genuinely open-ended. Then end your turn." : "Nothing further is requested \u2014 make no other changes unless the user asks, and end your turn.";
8772
+ return flattenLine(
8773
+ `[Argent Lens] Feedback from the preview window (round ${o2.round}). ${body}. ` + applyChosen + closing
8774
+ );
8775
+ }
8776
+ function parseArgs(argv) {
8777
+ let terminal;
8778
+ let agent;
8779
+ let help = false;
8780
+ let forget2 = false;
8781
+ for (let i2 = 0; i2 < argv.length; i2++) {
8782
+ const tok = argv[i2];
8783
+ if (tok === "--help" || tok === "-h") help = true;
8784
+ else if (tok === "--forget") forget2 = true;
8785
+ else if (tok === "--terminal" || tok === "-t") {
8786
+ const v = argv[++i2];
8787
+ if (v === "iterm" || v === "terminal") terminal = v;
8788
+ else {
8789
+ process.stderr.write(`lens: --terminal expects "iterm" or "terminal", got "${v ?? ""}"
8790
+ `);
8791
+ process.exit(2);
8792
+ }
8793
+ } else if (tok === "--agent" || tok === "-a") {
8794
+ agent = argv[++i2];
8795
+ if (!agent) {
8796
+ process.stderr.write(`lens: --agent expects one of: ${agentIds().join(", ")}
8797
+ `);
8798
+ process.exit(2);
8799
+ }
8800
+ }
8801
+ }
8802
+ return { terminal, agent, help, forget: forget2 };
8803
+ }
8804
+ function printHelp() {
8805
+ process.stdout.write(
8806
+ `Usage: argent lens [--agent <id>] [--terminal iterm|terminal]
8807
+
8808
+ Open Argent Lens bound to a fresh coding-agent session.
8809
+
8810
+ Opens the Lens preview window and runs your agent IN THIS terminal (it takes
8811
+ over the current window). When you request changes in the preview window they
8812
+ are typed into that agent session as its next prompt. Pick the agent in the
8813
+ window when more than one is installed, or pass --agent. The window streams a
8814
+ running simulator, or offers an in-window picker to boot one. Lens ends when
8815
+ you close the agent.
8816
+
8817
+ If this terminal isn't iTerm or Terminal.app (e.g. tmux / VS Code), the agent
8818
+ opens in a new window instead.
8819
+
8820
+ Options:
8821
+ -a, --agent <id> Agent to bind: ${agentIds().join(", ")}
8822
+ -t, --terminal <app> Terminal for the new-window fallback (iterm preferred)
8823
+ --forget Forget the remembered agent, then exit
8824
+ -h, --help Show this help
8825
+ `
8826
+ );
8827
+ }
8828
+ async function lens(argv, options) {
8829
+ const { terminal: preferred, agent: agentId, help, forget: forget2 } = parseArgs(argv);
8830
+ if (help) {
8831
+ printHelp();
8832
+ return;
8833
+ }
8834
+ if (forget2) {
8835
+ const had = getRememberedAgent();
8836
+ clearRememberedAgent();
8837
+ process.stdout.write(
8838
+ had ? ` Forgot the remembered agent ("${had}"). The picker will show again next run.
8839
+ ` : " No agent was remembered.\n"
8840
+ );
8841
+ return;
8842
+ }
8843
+ if (process.platform !== "darwin") {
8844
+ process.stderr.write(
8845
+ "argent lens is macOS-only \u2014 it drives Terminal/iTerm via osascript to run and\nfeed the agent session.\n"
8846
+ );
8847
+ process.exit(1);
8848
+ }
8849
+ if (!isFlagEnabled("argent-lens")) {
8850
+ process.stderr.write(
8851
+ "Argent Lens is behind a feature flag. Enable it first:\n\n argent enable argent-lens\n\n"
8852
+ );
8853
+ process.exit(1);
8854
+ }
8855
+ const plan = planAgent(agentId);
8856
+ const choices = "choose" in plan ? plan.choose : [];
8857
+ const client2 = createToolsClient({ paths: options.paths });
8858
+ let baseUrl;
8079
8859
  try {
8080
- raw = fs8.readFileSync(filePath, "utf8");
8081
- } catch {
8082
- return {};
8860
+ baseUrl = (await client2.baseUrl()).url;
8861
+ } catch (err) {
8862
+ process.stderr.write(`lens: could not reach the tool-server: ${errMsg(err)}
8863
+ `);
8864
+ process.exit(1);
8083
8865
  }
8084
- let parsed;
8866
+ const previewUrl = `${baseUrl}/preview/`;
8085
8867
  try {
8086
- parsed = JSON.parse(raw);
8087
- } catch {
8088
- return {};
8868
+ const res = await fetch(`${baseUrl}/preview/cli-session`, {
8869
+ method: "POST",
8870
+ headers: { "Content-Type": "application/json" },
8871
+ body: JSON.stringify({
8872
+ active: true,
8873
+ agents: choices.map((a2) => ({ id: a2.id, name: a2.displayName }))
8874
+ })
8875
+ });
8876
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
8877
+ } catch (err) {
8878
+ process.stderr.write(`lens: failed to start the Lens session: ${errMsg(err)}
8879
+ `);
8880
+ process.exit(1);
8089
8881
  }
8090
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8091
- const flags2 = parsed.flags;
8092
- if (!flags2 || typeof flags2 !== "object" || Array.isArray(flags2)) return {};
8093
- const out = {};
8094
- for (const [k, v] of Object.entries(flags2)) {
8095
- if (typeof v === "boolean") out[k] = v;
8882
+ let agent = "agent" in plan ? plan.agent : void 0;
8883
+ if (!agent) {
8884
+ process.stdout.write(
8885
+ `
8886
+ Argent Lens window is open: ${previewUrl}
8887
+ Pick an agent in the window to start\u2026
8888
+ `
8889
+ );
8890
+ const picked = await awaitAgentChoiceViaStream(baseUrl);
8891
+ if (picked) {
8892
+ agent = findAgentById(picked.id);
8893
+ if (agent && picked.remember) setRememberedAgent(agent.id);
8894
+ }
8096
8895
  }
8097
- return out;
8098
- }
8099
- function writeFlagsFile(filePath, flags2) {
8100
- if (Object.keys(flags2).length === 0) {
8101
- if (fs8.existsSync(filePath)) fs8.rmSync(filePath, { force: true });
8102
- const parent = path9.dirname(filePath);
8896
+ if (!agent) {
8897
+ process.stderr.write("lens: no agent was chosen \u2014 closing the Lens session.\n");
8898
+ await endSession(baseUrl);
8899
+ process.exit(1);
8900
+ }
8901
+ const seedFile = path11.join(os2.tmpdir(), `argent-lens-seed-${process.pid}-${Date.now()}.txt`);
8902
+ fs9.writeFileSync(seedFile, buildSeedPrompt(), "utf8");
8903
+ const launchCmd = agent.launch(shellQuote(process.cwd()), shellQuote(seedFile));
8904
+ const removeSeedFile = () => {
8103
8905
  try {
8104
- if (fs8.existsSync(parent) && fs8.readdirSync(parent).length === 0) {
8105
- fs8.rmdirSync(parent);
8106
- }
8906
+ fs9.rmSync(seedFile, { force: true });
8107
8907
  } catch {
8108
8908
  }
8109
- return;
8909
+ };
8910
+ const ptyMod = isInteractiveTty() ? loadNodePty() : null;
8911
+ let inject;
8912
+ let registerDeath;
8913
+ let quiet = false;
8914
+ let killAgent = () => {
8915
+ };
8916
+ if (ptyMod) {
8917
+ let proxy;
8918
+ try {
8919
+ proxy = startPtyProxy({ pty: ptyMod, command: launchCmd, cwd: process.cwd() });
8920
+ } catch (err) {
8921
+ process.stderr.write(`lens: failed to start the agent PTY: ${errMsg(err)}
8922
+ `);
8923
+ removeSeedFile();
8924
+ await endSession(baseUrl);
8925
+ process.exit(1);
8926
+ }
8927
+ quiet = true;
8928
+ dismissTrustPromptViaPty(proxy);
8929
+ if (agent.injectSeed) void injectSeedViaPty(proxy);
8930
+ inject = (text2) => proxy.inject(text2);
8931
+ registerDeath = (onDeath) => proxy.onExit(() => onDeath());
8932
+ killAgent = () => proxy.dispose();
8933
+ } else {
8934
+ const term = resolveTerminal(preferred);
8935
+ let session;
8936
+ try {
8937
+ session = spawnTerminalSession(launchCmd, term);
8938
+ } catch (err) {
8939
+ process.stderr.write(`lens: failed to spawn the agent: ${errMsg(err)}
8940
+ `);
8941
+ removeSeedFile();
8942
+ await endSession(baseUrl);
8943
+ process.exit(1);
8944
+ }
8945
+ process.stdout.write(
8946
+ `
8947
+ Argent Lens is live.
8948
+
8949
+ \u2022 Agent: ${agent.displayName} (new window)
8950
+ \u2022 Preview window: ${previewUrl}
8951
+
8952
+ Ask the agent to redesign something; review the variants in the window and
8953
+ request changes \u2014 they're queued to the agent automatically. End it by
8954
+ closing the agent terminal.
8955
+
8956
+ `
8957
+ );
8958
+ void dismissTrustPrompt(session);
8959
+ if (agent.injectSeed) void injectSeedAfterBoot(session);
8960
+ inject = (text2) => writeToSession(session, text2);
8961
+ registerDeath = (onDeath) => pollSessionDeath(session, onDeath);
8962
+ }
8963
+ let tearingDown = false;
8964
+ const teardown = (code) => {
8965
+ if (tearingDown) return;
8966
+ tearingDown = true;
8967
+ killAgent();
8968
+ void endSession(baseUrl).finally(() => {
8969
+ removeSeedFile();
8970
+ process.exit(code);
8971
+ });
8972
+ };
8973
+ const onSignal = () => {
8974
+ teardown(0);
8975
+ };
8976
+ process.on("SIGINT", onSignal);
8977
+ process.on("SIGTERM", onSignal);
8978
+ await runRelaySession(baseUrl, inject, registerDeath, quiet);
8979
+ teardown(0);
8980
+ }
8981
+ var AGENT_CHOICE_MAX_RECONNECTS = 60;
8982
+ async function awaitAgentChoiceViaStream(baseUrl) {
8983
+ const ac = new AbortController();
8984
+ let reconnects = 0;
8985
+ try {
8986
+ while (!ac.signal.aborted && reconnects <= AGENT_CHOICE_MAX_RECONNECTS) {
8987
+ try {
8988
+ for await (const ev of lensEvents(baseUrl, ac.signal)) {
8989
+ reconnects = 0;
8990
+ if (ev.event === "session-end") return null;
8991
+ if (ev.event === "agent-choice") {
8992
+ try {
8993
+ const payload = JSON.parse(ev.data);
8994
+ if (payload && typeof payload.id === "string" && payload.id) {
8995
+ return { id: payload.id, remember: Boolean(payload.remember) };
8996
+ }
8997
+ } catch {
8998
+ }
8999
+ }
9000
+ }
9001
+ } catch {
9002
+ }
9003
+ reconnects++;
9004
+ if (ac.signal.aborted || reconnects > AGENT_CHOICE_MAX_RECONNECTS) break;
9005
+ await sleep2(SSE_RECONNECT_MS);
9006
+ }
9007
+ } finally {
9008
+ ac.abort();
8110
9009
  }
8111
- fs8.mkdirSync(path9.dirname(filePath), { recursive: true });
8112
- const tmp = `${filePath}.${process.pid}.tmp`;
8113
- fs8.writeFileSync(tmp, JSON.stringify({ flags: flags2 }, null, 2) + "\n");
8114
- fs8.renameSync(tmp, filePath);
9010
+ return null;
8115
9011
  }
8116
- function readFlags(scope, options = {}) {
8117
- return readFlagsFile(getFlagsPath(scope, options));
9012
+ function pollSessionDeath(session, onDeath) {
9013
+ const spawnedAt = Date.now();
9014
+ let deathStreak = 0;
9015
+ const timer = setInterval(() => {
9016
+ if (Date.now() - spawnedAt <= SPAWN_GRACE_MS) return;
9017
+ if (!isSessionAlive(session)) {
9018
+ if (++deathStreak >= DEATH_CONFIRMATIONS) {
9019
+ clearInterval(timer);
9020
+ onDeath();
9021
+ }
9022
+ } else {
9023
+ deathStreak = 0;
9024
+ }
9025
+ }, LIVENESS_POLL_MS);
9026
+ timer.unref?.();
8118
9027
  }
8119
- function setFlag(name, value, scope, options = {}) {
8120
- const filePath = getFlagsPath(scope, options);
8121
- const current = readFlagsFile(filePath);
8122
- current[name] = value;
8123
- writeFlagsFile(filePath, current);
9028
+ async function runRelaySession(baseUrl, inject, registerDeath, quiet) {
9029
+ const log2 = (s) => {
9030
+ if (!quiet) process.stdout.write(s);
9031
+ };
9032
+ const ac = new AbortController();
9033
+ let alive = true;
9034
+ const stop = () => {
9035
+ if (!alive) return;
9036
+ alive = false;
9037
+ ac.abort();
9038
+ };
9039
+ registerDeath(() => {
9040
+ log2("\n Agent exited.\n");
9041
+ stop();
9042
+ });
9043
+ let lastCompletedAt = 0;
9044
+ try {
9045
+ const seed = await fetchOutcomeOnce(baseUrl);
9046
+ if (seed) lastCompletedAt = seed.completedAt;
9047
+ } catch {
9048
+ }
9049
+ while (alive && !ac.signal.aborted) {
9050
+ try {
9051
+ for await (const ev of lensEvents(baseUrl, ac.signal)) {
9052
+ if (!alive) break;
9053
+ if (ev.event === "session-end") return;
9054
+ if (ev.event !== "outcome") continue;
9055
+ let outcome = null;
9056
+ try {
9057
+ outcome = JSON.parse(ev.data);
9058
+ } catch {
9059
+ continue;
9060
+ }
9061
+ if (outcome && outcome.status === "completed" && outcome.completedAt > lastCompletedAt) {
9062
+ lastCompletedAt = outcome.completedAt;
9063
+ const ok = inject(formatLensFeedback(outcome));
9064
+ if (ok) {
9065
+ const n2 = outcome.selections.filter((s) => s.chosenVariant).length;
9066
+ log2(` \u2192 relayed feedback to the agent (${n2} pick(s)).
9067
+ `);
9068
+ } else {
9069
+ log2(" ! could not reach the agent (it may have exited).\n");
9070
+ }
9071
+ }
9072
+ }
9073
+ } catch {
9074
+ }
9075
+ if (alive && !ac.signal.aborted) await sleep2(SSE_RECONNECT_MS);
9076
+ }
8124
9077
  }
8125
- function unsetFlag(name, scope, options = {}) {
8126
- const filePath = getFlagsPath(scope, options);
8127
- const current = readFlagsFile(filePath);
8128
- if (!Object.hasOwn(current, name)) return false;
8129
- delete current[name];
8130
- writeFlagsFile(filePath, current);
8131
- return true;
9078
+ async function fetchOutcomeOnce(baseUrl) {
9079
+ const res = await fetch(`${baseUrl}/preview/outcome`, { cache: "no-store" });
9080
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
9081
+ const body = await res.json();
9082
+ return body.outcome && body.outcome.status === "completed" ? body.outcome : null;
8132
9083
  }
8133
- function isFlagEnabled(name, options = {}) {
8134
- const projectFlags = readFlags("project", options);
8135
- if (Object.hasOwn(projectFlags, name)) return projectFlags[name];
8136
- const globalFlags = readFlags("global", options);
8137
- if (Object.hasOwn(globalFlags, name)) return globalFlags[name];
8138
- return false;
9084
+ async function endSession(baseUrl) {
9085
+ try {
9086
+ await fetch(`${baseUrl}/preview/cli-session`, {
9087
+ method: "POST",
9088
+ headers: { "Content-Type": "application/json" },
9089
+ body: JSON.stringify({ active: false })
9090
+ });
9091
+ } catch {
9092
+ }
8139
9093
  }
8140
9094
 
8141
9095
  // ../argent-cli/src/flags.ts
9096
+ var import_picocolors = __toESM(require_picocolors(), 1);
8142
9097
  function colorState(enabled) {
8143
9098
  const label = (enabled ? "enabled" : "disabled").padEnd(8);
8144
9099
  if (!process.stdout.isTTY) return label;
@@ -8468,7 +9423,7 @@ var fastStringWidth = (input, options = {}) => {
8468
9423
  var dist_default2 = fastStringWidth;
8469
9424
 
8470
9425
  // ../../node_modules/fast-wrap-ansi/lib/main.js
8471
- var ESC = "\x1B";
9426
+ var ESC2 = "\x1B";
8472
9427
  var CSI = "\x9B";
8473
9428
  var END_CODE = 39;
8474
9429
  var ANSI_ESCAPE_BELL = "\x07";
@@ -8502,8 +9457,8 @@ var getClosingCode = (openingCode) => {
8502
9457
  return 0;
8503
9458
  return void 0;
8504
9459
  };
8505
- var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
8506
- var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
9460
+ var wrapAnsiCode = (code) => `${ESC2}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
9461
+ var wrapAnsiHyperlink = (url) => `${ESC2}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
8507
9462
  var wrapWord = (rows, word, columns) => {
8508
9463
  const characters = word[Symbol.iterator]();
8509
9464
  let isInsideEscape = false;
@@ -8522,7 +9477,7 @@ var wrapWord = (rows, word, columns) => {
8522
9477
  rows.push(character);
8523
9478
  visible = 0;
8524
9479
  }
8525
- if (character === ESC || character === CSI) {
9480
+ if (character === ESC2 || character === CSI) {
8526
9481
  isInsideEscape = true;
8527
9482
  isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
8528
9483
  }
@@ -8640,7 +9595,7 @@ var exec = (string, columns, options = {}) => {
8640
9595
  } else {
8641
9596
  inSurrogate = false;
8642
9597
  }
8643
- if (character === ESC || character === CSI) {
9598
+ if (character === ESC2 || character === CSI) {
8644
9599
  GROUP_REGEX.lastIndex = i2 + 1;
8645
9600
  const groupsResult = GROUP_REGEX.exec(preString);
8646
9601
  const groups = groupsResult?.groups;
@@ -9052,8 +10007,8 @@ var n = class extends V {
9052
10007
  import { styleText as styleText2, stripVTControlCharacters } from "node:util";
9053
10008
  import process$1 from "node:process";
9054
10009
  var import_sisteransi2 = __toESM(require_src(), 1);
9055
- import { existsSync as existsSync5, lstatSync as lstatSync3, readdirSync as readdirSync2 } from "node:fs";
9056
- import { dirname as dirname5, join as join9 } from "node:path";
10010
+ import { existsSync as existsSync6, lstatSync as lstatSync3, readdirSync as readdirSync2 } from "node:fs";
10011
+ import { dirname as dirname5, join as join11 } from "node:path";
9057
10012
  function isUnicodeSupported() {
9058
10013
  if (process$1.platform !== "win32") {
9059
10014
  return process$1.env.TERM !== "linux";
@@ -10024,6 +10979,7 @@ export {
10024
10979
  flags,
10025
10980
  getFlagDefinition,
10026
10981
  isFlagEnabled,
10982
+ lens,
10027
10983
  link,
10028
10984
  run,
10029
10985
  server,