xovris 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/xovris.mjs CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
- import { B as readsForShape, E as undoFixes, F as leaveBy, G as assumptionsOf, I as forAgents, J as newFileDiff, K as probeNames, L as readsForReview, M as applyGuarded, O as guardStrip, P as handOff, R as reviewChange, T as commandRunner, U as dirReader, V as testsFirst, W as workOut, Y as parseDiff, h as revertOne, j as valueCopy, m as fixAll, q as readingsOf, z as stackChain } from "./src-DPO9_Gtz.js";
1
+ import { B as readsForShape, E as undoFixes, F as leaveBy, G as workOut, H as refreshDeprecations, I as forAgents, J as readingsOf, K as assumptionsOf, L as readsForReview, M as applyGuarded, O as guardStrip, P as handOff, R as reviewChange, T as commandRunner, V as testsFirst, W as dirReader, X as parseDiff, Y as newFileDiff, h as revertOne, j as valueCopy, m as fixAll, q as probeNames, z as stackChain } from "./src-CZstFB4j.js";
2
2
  import { a as whoami, c as Cancelled, d as ApiFailure, f as CLI_AGENT, l as clackUi, n as login, o as writeFileAtomic, p as CLI_VERSION, r as logout, s as signIn, t as apiCommand, u as Api } from "./apicmd-3jWpFhG3.js";
3
- import { a as gatedWriteFile, i as gatedWrite, n as gatedRun, t as MANIFEST_FILES } from "./gated-write-DRftDST4.js";
4
- import { i as splitCommandLine, n as collectorAt, r as joinCollector, t as addExporter } from "./collector-join-CwjwINuL.js";
5
- import { a as reviewSarif, i as reviewJson, n as progressLine, r as renderReview, s as styleFor, t as leftToFix } from "./review-ui-DsFuGF7B.js";
3
+ import { a as gatedWriteFile, i as gatedWrite, n as gatedRun, t as MANIFEST_FILES } from "./gated-write-B8Aln6GU.js";
4
+ import { i as splitCommandLine, n as collectorAt, r as joinCollector, t as addExporter } from "./collector-join-CkBmhc2w.js";
5
+ import { a as reviewSarif, i as reviewJson, n as progressLine, r as renderReview, s as styleFor, t as leftToFix } from "./review-ui-DGEc-h8N.js";
6
6
  import { builtinModules } from "node:module";
7
7
  import { mkdir, mkdtemp, readFile, readdir, rm, stat, unlink, writeFile } from "node:fs/promises";
8
8
  import { homedir, tmpdir } from "node:os";
@@ -1085,6 +1085,12 @@ function projectIngestUrl(ingestUrl, projectId) {
1085
1085
 
1086
1086
  //#endregion
1087
1087
  //#region ../stacks/src/agents.ts
1088
+ const MCP_SERVER_NAME = "xovris";
1089
+ /** UL-21: one remote MCP server for every AI tool, at its own host. */
1090
+ const DEFAULT_MCP_ORIGIN = "https://api.xovris.ai";
1091
+ function mcpUrl(apiOrigin = DEFAULT_MCP_ORIGIN) {
1092
+ return new URL("/v1/mcp", apiOrigin).toString();
1093
+ }
1088
1094
  const AGENT_CLIENTS = [
1089
1095
  {
1090
1096
  id: "claude_code",
@@ -1150,6 +1156,145 @@ const AGENT_CLIENTS = [
1150
1156
  kind: "config"
1151
1157
  }
1152
1158
  ];
1159
+ const b64 = (s) => btoa(String.fromCharCode(...new TextEncoder().encode(s)));
1160
+ /** Each client's documented way to add a remote (Streamable HTTP) server; OAuth sign-in happens in the client. */
1161
+ function agentInstall(client, url = mcpUrl()) {
1162
+ switch (client) {
1163
+ case "claude_code": return `claude mcp add --transport http ${MCP_SERVER_NAME} ${url}`;
1164
+ case "codex": return `codex mcp add ${MCP_SERVER_NAME} --url ${url} && codex mcp login ${MCP_SERVER_NAME}`;
1165
+ case "copilot_cli": return `copilot mcp add --transport http ${MCP_SERVER_NAME} ${url}`;
1166
+ case "lm_studio": return `lmstudio://add_mcp?name=${MCP_SERVER_NAME}&config=${encodeURIComponent(b64(JSON.stringify({ url })))}`;
1167
+ case "goose": return `goose://extension?url=${encodeURIComponent(url)}&type=streamable_http&id=${MCP_SERVER_NAME}&name=Xovris&description=${encodeURIComponent("Xovris setup, incidents and issues")}`;
1168
+ case "vscode": return `code --add-mcp '${JSON.stringify({
1169
+ name: MCP_SERVER_NAME,
1170
+ type: "http",
1171
+ url
1172
+ })}'`;
1173
+ case "cursor": return `cursor://anysphere.cursor-deeplink/mcp/install?name=${MCP_SERVER_NAME}&config=${encodeURIComponent(b64(JSON.stringify({ url })))}`;
1174
+ case "windsurf": return JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: { serverUrl: url } } }, null, 2);
1175
+ case "gemini_cli": return `gemini mcp add -t http -s user ${MCP_SERVER_NAME} ${url}`;
1176
+ case "chatgpt":
1177
+ case "claude_app": return url;
1178
+ case "other": return JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: { url } } }, null, 2);
1179
+ }
1180
+ }
1181
+ /**
1182
+ * G138.13: the same documented adds as argument lists a program runs (npx xovris init, after one yes) - only the clients whose add is a
1183
+ * command; a link or a settings block is opened or merged instead, and an app with no command is null.
1184
+ */
1185
+ function agentInstallArgs(client, url = mcpUrl()) {
1186
+ switch (client) {
1187
+ case "claude_code": return [{
1188
+ cmd: "claude",
1189
+ args: [
1190
+ "mcp",
1191
+ "add",
1192
+ "--transport",
1193
+ "http",
1194
+ MCP_SERVER_NAME,
1195
+ url
1196
+ ]
1197
+ }];
1198
+ case "codex": return [{
1199
+ cmd: "codex",
1200
+ args: [
1201
+ "mcp",
1202
+ "add",
1203
+ MCP_SERVER_NAME,
1204
+ "--url",
1205
+ url
1206
+ ]
1207
+ }, {
1208
+ cmd: "codex",
1209
+ args: [
1210
+ "mcp",
1211
+ "login",
1212
+ MCP_SERVER_NAME
1213
+ ]
1214
+ }];
1215
+ case "copilot_cli": return [{
1216
+ cmd: "copilot",
1217
+ args: [
1218
+ "mcp",
1219
+ "add",
1220
+ "--transport",
1221
+ "http",
1222
+ MCP_SERVER_NAME,
1223
+ url
1224
+ ]
1225
+ }];
1226
+ case "vscode": return [{
1227
+ cmd: "code",
1228
+ args: ["--add-mcp", JSON.stringify({
1229
+ name: MCP_SERVER_NAME,
1230
+ type: "http",
1231
+ url
1232
+ })]
1233
+ }];
1234
+ case "gemini_cli": return [{
1235
+ cmd: "gemini",
1236
+ args: [
1237
+ "mcp",
1238
+ "add",
1239
+ "-t",
1240
+ "http",
1241
+ "-s",
1242
+ "user",
1243
+ MCP_SERVER_NAME,
1244
+ url
1245
+ ]
1246
+ }];
1247
+ default: return null;
1248
+ }
1249
+ }
1250
+ /** NAA-27: the one package every plugin host installs (https://github.com/xovris/agent-plugins, published from packages/agent-plugins). */
1251
+ const PLUGIN_SOURCE = "xovris/agent-plugins";
1252
+ /** NAA-27: a host whose own plugin install brings Xovris whole - its tools and its way of working, from the one package - with the host's
1253
+ * own list that reads it back; null for a host that is given the MCP server alone (agentInstallArgs). */
1254
+ function agentPluginArgs(client) {
1255
+ switch (client) {
1256
+ case "claude_code": return {
1257
+ steps: [{
1258
+ cmd: "claude",
1259
+ args: [
1260
+ "plugin",
1261
+ "marketplace",
1262
+ "add",
1263
+ PLUGIN_SOURCE
1264
+ ]
1265
+ }, {
1266
+ cmd: "claude",
1267
+ args: [
1268
+ "plugin",
1269
+ "install",
1270
+ `${MCP_SERVER_NAME}@${MCP_SERVER_NAME}`
1271
+ ]
1272
+ }],
1273
+ check: {
1274
+ cmd: "claude",
1275
+ args: ["plugin", "list"],
1276
+ names: `${MCP_SERVER_NAME}@${MCP_SERVER_NAME}`
1277
+ }
1278
+ };
1279
+ case "gemini_cli": return {
1280
+ steps: [{
1281
+ cmd: "gemini",
1282
+ args: [
1283
+ "extensions",
1284
+ "install",
1285
+ `https://github.com/${PLUGIN_SOURCE}`,
1286
+ "--consent"
1287
+ ]
1288
+ }],
1289
+ check: {
1290
+ cmd: "gemini",
1291
+ args: ["extensions", "list"],
1292
+ names: MCP_SERVER_NAME
1293
+ }
1294
+ };
1295
+ default: return null;
1296
+ }
1297
+ }
1153
1298
 
1154
1299
  //#endregion
1155
1300
  //#region ../stacks/src/status.ts
@@ -1239,7 +1384,7 @@ function doorStatus(e) {
1239
1384
  };
1240
1385
  return {
1241
1386
  status: "unverified",
1242
- why: `The ${e.name} connection is built and passes Xovris's own tests against ${e.name}'s published interface, but has not yet run against ${e.name} itself.`,
1387
+ why: `Connect ${e.name} and Xovris confirms it works on its first real check.`,
1243
1388
  evidenceAt: null,
1244
1389
  offered: false
1245
1390
  };
@@ -3632,6 +3777,215 @@ async function checksCommand(sub, o, ui, sys, secrets) {
3632
3777
  }
3633
3778
  }
3634
3779
 
3780
+ //#endregion
3781
+ //#region src/agents-init.ts
3782
+ function machinePlaces(env = process.env, platform = process.platform) {
3783
+ const home = homedir();
3784
+ return {
3785
+ home,
3786
+ vscodeUser: platform === "win32" ? join(env.APPDATA ?? join(home, "AppData", "Roaming"), "Code", "User") : platform === "darwin" ? join(home, "Library", "Application Support", "Code", "User") : join(env.XDG_CONFIG_HOME ?? join(home, ".config"), "Code", "User"),
3787
+ exists: (p) => existsSync(p),
3788
+ read: (p) => {
3789
+ try {
3790
+ return readFileSync(p, "utf8");
3791
+ } catch {
3792
+ return null;
3793
+ }
3794
+ },
3795
+ write: (p, t) => {
3796
+ mkdirSync(dirname(p), { recursive: true });
3797
+ writeFileSync(p, t);
3798
+ }
3799
+ };
3800
+ }
3801
+ /** The command each command-line client answers to, and its MCP list (read to see whether Xovris is already there). */
3802
+ const COMMANDS = {
3803
+ claude_code: "claude",
3804
+ codex: "codex",
3805
+ gemini_cli: "gemini",
3806
+ copilot_cli: "copilot",
3807
+ vscode: "code"
3808
+ };
3809
+ const holds = (text) => text !== null && (/* @__PURE__ */ new RegExp(`["'\\s\\[.]${MCP_SERVER_NAME}["'\\s\\].=:]`)).test(text);
3810
+ const settings = (p) => ({
3811
+ cursor: {
3812
+ folder: join(p.home, ".cursor"),
3813
+ file: join(p.home, ".cursor", "mcp.json")
3814
+ },
3815
+ windsurf: {
3816
+ folder: join(p.home, ".codeium", "windsurf"),
3817
+ file: join(p.home, ".codeium", "windsurf", "mcp_config.json")
3818
+ },
3819
+ vscode: {
3820
+ folder: p.vscodeUser,
3821
+ file: join(p.vscodeUser, "mcp.json")
3822
+ }
3823
+ });
3824
+ /** Every coding agent on this machine, found by itself - a command that answers, or the client's own settings folder. */
3825
+ async function detectAgentClients(sys, places, api) {
3826
+ const url = mcpUrl(api);
3827
+ const where = settings(places);
3828
+ const out = [];
3829
+ for (const c of AGENT_CLIENTS) {
3830
+ const cmd = COMMANDS[c.id];
3831
+ const own = where[c.id];
3832
+ let present = false;
3833
+ let already = false;
3834
+ if (cmd) {
3835
+ present = (await sys.run(cmd, ["--version"], places.home).catch(() => null))?.code === 0;
3836
+ if (present && c.id !== "vscode") {
3837
+ const l = await sys.run(cmd, ["mcp", "list"], places.home).catch(() => null);
3838
+ already = l?.code === 0 && holds(` ${l.stdout} `);
3839
+ }
3840
+ }
3841
+ if (own && places.exists(own.folder)) present = true;
3842
+ if (!present) continue;
3843
+ if (own) already ||= holds(places.read(own.file));
3844
+ out.push({
3845
+ id: c.id,
3846
+ name: c.name,
3847
+ kind: c.kind,
3848
+ line: agentInstall(c.id, url),
3849
+ already
3850
+ });
3851
+ }
3852
+ return out;
3853
+ }
3854
+ /** NAA-27: the host's own plugin install, each step answered and the host's own list reading Xovris back - never counted on its word. */
3855
+ async function pluginAdded(sys, cwd, p) {
3856
+ for (const s of p.steps) {
3857
+ const r = await sys.run(s.cmd, s.args, cwd).catch(() => ({
3858
+ code: 1,
3859
+ stdout: "",
3860
+ stderr: ""
3861
+ }));
3862
+ if (r.code !== 0 && !/already/i.test(`${r.stdout}\n${r.stderr}`)) return false;
3863
+ }
3864
+ const l = await sys.run(p.check.cmd, p.check.args, cwd).catch(() => null);
3865
+ return l?.code === 0 && l.stdout.includes(p.check.names);
3866
+ }
3867
+ /** Windsurf (and any client whose add is a settings block): Xovris merged into its own file, every server already there kept. */
3868
+ function mergeSettings(places, file, key, url) {
3869
+ const text = places.read(file);
3870
+ let doc = {};
3871
+ if (text && text.trim()) {
3872
+ try {
3873
+ doc = JSON.parse(text);
3874
+ } catch {
3875
+ return `its settings file ${file} is not JSON Xovris can read, so it was left as it was`;
3876
+ }
3877
+ if (!doc || typeof doc !== "object" || Array.isArray(doc)) return `its settings file ${file} is not a settings object, so it was left as it was`;
3878
+ }
3879
+ places.write(file, `${JSON.stringify({
3880
+ ...doc,
3881
+ mcpServers: {
3882
+ ...doc.mcpServers ?? {},
3883
+ [MCP_SERVER_NAME]: { [key]: url }
3884
+ }
3885
+ }, null, 2)}\n`);
3886
+ return null;
3887
+ }
3888
+ /**
3889
+ * G138.13: the offer itself - every client found said with its own add, ONE question for all of them, then each added its own way and
3890
+ * read back (a command's exit code and words, a link opened, a settings file written and read again). A no changes nothing.
3891
+ */
3892
+ async function offerAgents(o, ui, sys, d) {
3893
+ const places = d.places ?? machinePlaces();
3894
+ const found = await detectAgentClients(sys, places, o.api);
3895
+ const todo = found.filter((f) => !f.already);
3896
+ const names = (xs) => xs.map((x) => x.name).join(", ");
3897
+ if (!found.length) return {
3898
+ found,
3899
+ added: [],
3900
+ failed: [],
3901
+ asked: false
3902
+ };
3903
+ if (!todo.length) {
3904
+ ui.info(`Xovris is already in ${names(found)} on this machine.`);
3905
+ return {
3906
+ found,
3907
+ added: [],
3908
+ failed: [],
3909
+ asked: false
3910
+ };
3911
+ }
3912
+ ui.note(todo.map((f) => `${f.name}: ${agentPluginArgs(f.id) ? `installs the Xovris plugin (its tools and its way of working), or adds ${f.line}` : f.kind === "config" ? `adds Xovris to its MCP settings` : f.line}`).join("\n"), "Coding agents on this machine");
3913
+ if (!await d.ask(`Add Xovris to ${names(todo)} now, each by its own add?`)) {
3914
+ ui.info("Nothing was added to your coding agents. npx xovris agents offers it again.");
3915
+ return {
3916
+ found,
3917
+ added: [],
3918
+ failed: [],
3919
+ asked: true
3920
+ };
3921
+ }
3922
+ const url = mcpUrl(o.api);
3923
+ const added = [];
3924
+ const failed = [];
3925
+ for (const f of todo) {
3926
+ const plug = agentPluginArgs(f.id);
3927
+ if (plug && await pluginAdded(sys, places.home, plug)) {
3928
+ added.push(`${f.name} (the Xovris plugin)`);
3929
+ continue;
3930
+ }
3931
+ const steps = agentInstallArgs(f.id, url);
3932
+ if (steps) {
3933
+ let why = null;
3934
+ for (const s of steps) {
3935
+ const r = await sys.run(s.cmd, s.args, places.home).catch((e) => ({
3936
+ code: 1,
3937
+ stdout: "",
3938
+ stderr: String(e?.message ?? e)
3939
+ }));
3940
+ if (r.code !== 0 && !/already exists|already configured|already added/i.test(`${r.stdout}\n${r.stderr}`)) {
3941
+ why = (r.stderr || r.stdout).trim().split(/\r?\n/)[0]?.slice(0, 200) || `${s.cmd} answered ${r.code}`;
3942
+ break;
3943
+ }
3944
+ }
3945
+ if (why) failed.push({
3946
+ name: f.name,
3947
+ why
3948
+ });
3949
+ else added.push(f.name);
3950
+ continue;
3951
+ }
3952
+ if (f.kind === "link") {
3953
+ if (await sys.openUrl(f.line)) added.push(`${f.name} (its install link is open - press Install there)`);
3954
+ else failed.push({
3955
+ name: f.name,
3956
+ why: `Xovris opens its install link for you, and this machine has no browser to open it in. Do it yourself instead: ${f.line}`
3957
+ });
3958
+ continue;
3959
+ }
3960
+ const own = settings(places)[f.id];
3961
+ if (own && f.kind === "config") {
3962
+ const wrong = mergeSettings(places, own.file, f.id === "windsurf" ? "serverUrl" : "url", url);
3963
+ if (wrong) failed.push({
3964
+ name: f.name,
3965
+ why: wrong
3966
+ });
3967
+ else if (holds(places.read(own.file))) added.push(f.name);
3968
+ else failed.push({
3969
+ name: f.name,
3970
+ why: `its settings file ${own.file} did not read back with Xovris in it`
3971
+ });
3972
+ continue;
3973
+ }
3974
+ failed.push({
3975
+ name: f.name,
3976
+ why: `it is added in the app itself: ${f.line}`
3977
+ });
3978
+ }
3979
+ if (added.length) ui.success(`Xovris added to ${added.join(", ")}. Each signs in to Xovris the first time it uses it.`);
3980
+ for (const x of failed) ui.warn(`${x.name}: ${x.why}`);
3981
+ return {
3982
+ found,
3983
+ added,
3984
+ failed,
3985
+ asked: true
3986
+ };
3987
+ }
3988
+
3635
3989
  //#endregion
3636
3990
  //#region src/instrumentation.ts
3637
3991
  const MARK = "Added by npx xovris init - remove with npx xovris remove";
@@ -5927,13 +6281,33 @@ function recordInstallFailure(manager, output, dir = process.env.XOVRIS_INSTALL_
5927
6281
 
5928
6282
  //#endregion
5929
6283
  //#region src/system.ts
6284
+ const META = /([()\][%!^"`<>&|;, *?])/g;
6285
+ const forCmd = (a, shim) => {
6286
+ const q = `"${a.replace(/(\\*)"/g, "$1$1\\\"").replace(/(\\*)$/, "$1$1")}"`.replace(META, "^$1");
6287
+ return shim ? q.replace(META, "^$1") : q;
6288
+ };
6289
+ function onPath(cmd) {
6290
+ if (/[\\/]/.test(cmd) || /\.\w+$/.test(cmd)) return cmd;
6291
+ const exts$1 = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";");
6292
+ for (const dir of (process.env.PATH ?? "").split(";")) for (const e of exts$1) if (dir && existsSync(join(dir, cmd + e))) return join(dir, cmd + e);
6293
+ return cmd;
6294
+ }
6295
+ function commandLine(file, args) {
6296
+ const shim = /\.(cmd|bat)$/i.test(file);
6297
+ return [/[\s()&^|<>]/.test(file) ? `"${file.replace(/"/g, "")}"` : file.replace(META, "^$1"), ...args.map((a) => forCmd(a, shim))].join(" ");
6298
+ }
6299
+ function launch(cmd, args, opts) {
6300
+ if (process.platform !== "win32") return spawn(cmd, args, opts);
6301
+ return spawn(commandLine(onPath(cmd), args), {
6302
+ ...opts,
6303
+ shell: true
6304
+ });
6305
+ }
5930
6306
  function realSystem() {
5931
6307
  return {
5932
6308
  run: (cmd, args, cwd, env) => new Promise((resolve$1) => {
5933
- const win = process.platform === "win32";
5934
- const child = spawn(cmd, win ? args.map((a) => /[\s&|<>^()]/.test(a) && !a.includes("\"") ? `"${a}"` : a) : args, {
6309
+ const child = launch(cmd, args, {
5935
6310
  cwd,
5936
- shell: win,
5937
6311
  stdio: [
5938
6312
  "ignore",
5939
6313
  "pipe",
@@ -5946,10 +6320,10 @@ function realSystem() {
5946
6320
  });
5947
6321
  let stdout = "";
5948
6322
  let stderr = "";
5949
- child.stdout.on("data", (d) => {
6323
+ child.stdout?.on("data", (d) => {
5950
6324
  stdout += d;
5951
6325
  });
5952
- child.stderr.on("data", (d) => {
6326
+ child.stderr?.on("data", (d) => {
5953
6327
  stderr += d;
5954
6328
  });
5955
6329
  child.on("error", (e) => resolve$1({
@@ -5964,10 +6338,8 @@ function realSystem() {
5964
6338
  }));
5965
6339
  }),
5966
6340
  handOver: (cmd, args, cwd) => new Promise((resolve$1) => {
5967
- const win = process.platform === "win32";
5968
- const child = spawn(cmd, win ? args.map((a) => /[\s&|<>^()]/.test(a) && !a.includes("\"") ? `"${a}"` : a) : args, {
6341
+ const child = launch(cmd, args, {
5969
6342
  cwd,
5970
- shell: win,
5971
6343
  stdio: "inherit"
5972
6344
  });
5973
6345
  child.on("error", () => resolve$1(127));
@@ -6054,7 +6426,7 @@ async function commitUntracked(sys, cwd, path) {
6054
6426
  * backup proven on a copy, the commit made, read back by `holds`, and the tip put back by itself when it does not read back (the
6055
6427
  * files stay staged, so nothing is lost) - never over a commit someone else made after it.
6056
6428
  */
6057
- async function commitThroughGate(sys, cwd, id, message, holds, notHeld) {
6429
+ async function commitThroughGate(sys, cwd, id, message, holds$1, notHeld) {
6058
6430
  let known = null;
6059
6431
  const head = async () => known ??= (await sys.run("git", [
6060
6432
  "rev-parse",
@@ -6071,7 +6443,7 @@ async function commitThroughGate(sys, cwd, id, message, holds, notHeld) {
6071
6443
  changeDigest: `${id}\n${message}`,
6072
6444
  withinGrant: true,
6073
6445
  irreversible: false,
6074
- holds: async () => made ? holds() : false,
6446
+ holds: async () => made ? holds$1() : false,
6075
6447
  apply: async () => {
6076
6448
  known = null;
6077
6449
  const r = await sys.run("git", [
@@ -6499,6 +6871,11 @@ async function latestReach(sys, cwd) {
6499
6871
  }
6500
6872
  /** The wizard run with its one self-repair: a request the server reads as from an older wizard hands over to the latest. */
6501
6873
  async function initWithLatest(o, ui, sys, secrets) {
6874
+ const code = await initOnce(o, ui, sys, secrets);
6875
+ if (!o.ci && (process.stdin.isTTY || o.agents)) await offerAgents(o, ui, sys, { ask: (m) => ask(o, ui, m) }).catch(() => null);
6876
+ return code;
6877
+ }
6878
+ async function initOnce(o, ui, sys, secrets) {
6502
6879
  try {
6503
6880
  return await init(o, ui, sys, secrets);
6504
6881
  } catch (e) {
@@ -6508,6 +6885,12 @@ async function initWithLatest(o, ui, sys, secrets) {
6508
6885
  return code;
6509
6886
  }
6510
6887
  }
6888
+ /** G138.13: `npx xovris agents` - the same offer on its own, any time. */
6889
+ async function agentsCommand(o, ui, sys) {
6890
+ const r = await offerAgents(o, ui, sys, { ask: (m) => ask(o, ui, m) });
6891
+ if (!r.found.length) ui.info("No coding agent was found on this machine - Claude Code, Codex, Gemini CLI, Copilot CLI, VS Code, Cursor and Windsurf were each looked for.");
6892
+ return r.failed.length ? 1 : 0;
6893
+ }
6511
6894
  /** G105.01.C129: after an install the lockfile was let to change for, the manifest and lockfile are committed alone - CI then
6512
6895
  * installs from them with its lockfile frozen, exactly as here. Other staged work is never swept in; nothing is pushed. */
6513
6896
  async function commitDependencyFiles(cwd, manifest, ui, sys) {
@@ -7675,6 +8058,7 @@ async function reviewCommand(o, sys, out) {
7675
8058
  } : r;
7676
8059
  }
7677
8060
  progress.step("Reviewing");
8061
+ await refreshDeprecations().catch(() => null);
7678
8062
  const contents = {};
7679
8063
  const tracked = (await sys.run("git", ["ls-files", "-z"], o.cwd)).stdout.split("\0").filter(Boolean);
7680
8064
  const manifests = tracked.filter(readsForShape);
@@ -8146,6 +8530,7 @@ async function main(argv) {
8146
8530
  wait: flag("--wait"),
8147
8531
  localOnly: flag("--local-only"),
8148
8532
  undo: flag("--undo"),
8533
+ ...flag("--agents") ? { agents: true } : {},
8149
8534
  ...value("--repo") ? { repo: value("--repo") } : {},
8150
8535
  ...value("--connection") ? { connection: value("--connection") } : {},
8151
8536
  ci: !!process.env.CI,
@@ -8177,7 +8562,7 @@ async function main(argv) {
8177
8562
  }, ui, sys, secrets, io), ui);
8178
8563
  case "logout": return logout(io);
8179
8564
  case "sourcemaps": {
8180
- const { sayUpload, uploadSourceMaps } = await import("./sourcemaps-tbnp_n01.js");
8565
+ const { sayUpload, uploadSourceMaps } = await import("./sourcemaps-B7Knrrs4.js");
8181
8566
  const dir = positional[1] ?? "dist";
8182
8567
  let isDir$2 = false;
8183
8568
  try {
@@ -8214,6 +8599,10 @@ async function main(argv) {
8214
8599
  idempotency: !flag("--no-idempotency")
8215
8600
  }, { api: o.api }, sys, secrets, io);
8216
8601
  case "init": return guarded(() => initWithLatest(o, ui, sys, secrets), ui);
8602
+ case "agents": return guarded(() => agentsCommand({
8603
+ ...o,
8604
+ agents: true
8605
+ }, ui, sys), ui);
8217
8606
  case "discover": return guarded(() => discoverCommand(o, ui, sys, flag("--json") ? io : null), ui);
8218
8607
  case "scan": return guarded(() => scan(positional[1], ui, sys), ui);
8219
8608
  case "receipt": return guarded(() => receiptCommand(positional[1], positional[2], value("--key"), ui, sys), ui);
@@ -8247,7 +8636,7 @@ async function main(argv) {
8247
8636
  stack: flag("--stack")
8248
8637
  }, sys, io.out);
8249
8638
  case "merge": {
8250
- const { mergeCommand } = await import("./merge-BK7G5i5Q.js");
8639
+ const { mergeCommand } = await import("./merge-B-lFy27Y.js");
8251
8640
  return mergeCommand({
8252
8641
  cwd: o.cwd,
8253
8642
  entries: positional.slice(1),
@@ -8260,7 +8649,7 @@ async function main(argv) {
8260
8649
  }
8261
8650
  case "tests":
8262
8651
  case "touches": {
8263
- const f = await import("./finishing-Dsc2jn0-.js");
8652
+ const f = await import("./finishing-CZ013_fT.js");
8264
8653
  const fo = {
8265
8654
  cwd: o.cwd,
8266
8655
  module: positional[1] ?? "",
@@ -8270,7 +8659,7 @@ async function main(argv) {
8270
8659
  };
8271
8660
  return command === "tests" ? f.testsCommand(fo, io.out) : f.touchesCommand(fo, io.out);
8272
8661
  }
8273
- case "resolve": return (await import("./finishing-Dsc2jn0-.js")).resolveCommand({
8662
+ case "resolve": return (await import("./finishing-CZ013_fT.js")).resolveCommand({
8274
8663
  cwd: o.cwd,
8275
8664
  module: "",
8276
8665
  files: positional.slice(1),
@@ -8279,7 +8668,7 @@ async function main(argv) {
8279
8668
  test: many("--test", "--test")
8280
8669
  }, io.out);
8281
8670
  case "hunt": {
8282
- const { huntCommand } = await import("./hunt-BmQxPIL8.js");
8671
+ const { huntCommand } = await import("./hunt-Ce77c8DT.js");
8283
8672
  return huntCommand({
8284
8673
  cwd: positional[1] ?? o.cwd,
8285
8674
  env: process.env,
@@ -8290,7 +8679,7 @@ async function main(argv) {
8290
8679
  }, (t) => process.stdout.write(t));
8291
8680
  }
8292
8681
  case "code": {
8293
- const { codeCommand } = await import("./code-DsDiCUXb.js");
8682
+ const { codeCommand } = await import("./code-DKbqSLWT.js");
8294
8683
  return codeCommand({
8295
8684
  api: o.api,
8296
8685
  words: positional.slice(1).join(" "),
@@ -8299,7 +8688,7 @@ async function main(argv) {
8299
8688
  }, sys, io);
8300
8689
  }
8301
8690
  case "findings": {
8302
- const { findingsCommand } = await import("./findings-BHh24V38.js");
8691
+ const { findingsCommand } = await import("./findings-CcqH4Q2I.js");
8303
8692
  const read = {
8304
8693
  severity: value("--severity"),
8305
8694
  category: value("--category"),
@@ -8324,7 +8713,7 @@ async function main(argv) {
8324
8713
  }, { api: o.api }, sys, secrets, io);
8325
8714
  }
8326
8715
  case "languages": {
8327
- const { languagesCommand } = await import("./languages-DhPS_td-.js");
8716
+ const { languagesCommand } = await import("./languages-9JrWtbQN.js");
8328
8717
  return languagesCommand({
8329
8718
  gaps: flag("--gaps"),
8330
8719
  json: flag("--json"),
@@ -8332,7 +8721,7 @@ async function main(argv) {
8332
8721
  });
8333
8722
  }
8334
8723
  case "trace": {
8335
- const { traceCommand } = await import("./trace-DoQjPlBv.js");
8724
+ const { traceCommand } = await import("./trace-1RWrsTAT.js");
8336
8725
  return traceCommand({
8337
8726
  cwd: o.cwd,
8338
8727
  fix: flag("--fix"),
@@ -22,11 +22,17 @@ async function codeCommand(a, sys, io) {
22
22
  job: await r.json().catch(() => ({})) ?? {}
23
23
  };
24
24
  };
25
- const started = await call("POST", "/code/tasks", {
26
- words,
27
- ...a.repository ? { repository: a.repository } : {},
28
- via: "cli"
29
- });
25
+ let started;
26
+ try {
27
+ started = await call("POST", "/code/tasks", {
28
+ words,
29
+ ...a.repository ? { repository: a.repository } : {},
30
+ via: "cli"
31
+ });
32
+ } catch (e) {
33
+ io.err(`Xovris did not answer at ${plain(a.api, 200)} (${plain(e?.cause?.message ?? e?.message ?? e, 160)}). Nothing was started; check the address or your network and run it again.\n`);
34
+ return 1;
35
+ }
30
36
  if (started.status !== 202 || !started.job.id) {
31
37
  io.err(`${plain(started.job.title ?? `Xovris did not take the job (${started.status})`)}. ${plain(started.job.detail ?? "")}\n${plain(started.job.next ?? "Try again in a moment.")}\n`);
32
38
  return started.status === 400 ? 2 : 1;
@@ -1,4 +1,4 @@
1
- import { r as gatedService } from "./gated-write-DRftDST4.js";
1
+ import { r as gatedService } from "./gated-write-B8Aln6GU.js";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { isAbsolute, resolve } from "node:path";
4
4
  import { existsSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { X as SEVERITIES, c as FACET_AXES, l as checkFacetQuery, o as EFFORTS, s as EFFORT_UNMEASURED } from "./src-DPO9_Gtz.js";
1
+ import { Z as SEVERITIES, c as FACET_AXES, l as checkFacetQuery, o as EFFORTS, s as EFFORT_UNMEASURED } from "./src-CZstFB4j.js";
2
2
  import { i as tokenFor, u as Api } from "./apicmd-3jWpFhG3.js";
3
3
 
4
4
  //#region src/findings.ts
@@ -1,4 +1,4 @@
1
- import { A as contentDigest, N as jobRunner, d as generateTests, f as proveResolution, k as writeProven, p as touchesFor, u as applyTouches } from "./src-DPO9_Gtz.js";
1
+ import { A as contentDigest, N as jobRunner, d as generateTests, f as proveResolution, k as writeProven, p as touchesFor, u as applyTouches } from "./src-CZstFB4j.js";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { existsSync, readFileSync, readdirSync } from "node:fs";
4
4
  import { execFileSync } from "node:child_process";
@@ -1,4 +1,4 @@
1
- import { A as contentDigest, D as filesOf, M as applyGuarded, j as valueCopy, k as writeProven } from "./src-DPO9_Gtz.js";
1
+ import { A as contentDigest, D as filesOf, M as applyGuarded, j as valueCopy, k as writeProven } from "./src-CZstFB4j.js";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
@@ -1,5 +1,5 @@
1
- import { n as HUNT_CLASSES, t as huntRepository } from "./src-DPO9_Gtz.js";
2
- import { o as stripAnsi, s as styleFor } from "./review-ui-DsFuGF7B.js";
1
+ import { n as HUNT_CLASSES, t as huntRepository } from "./src-CZstFB4j.js";
2
+ import { o as stripAnsi, s as styleFor } from "./review-ui-DGEc-h8N.js";
3
3
  import { resolve } from "node:path";
4
4
  import { existsSync, statSync } from "node:fs";
5
5
 
@@ -1,4 +1,4 @@
1
- import { H as census } from "./src-DPO9_Gtz.js";
1
+ import { U as census } from "./src-CZstFB4j.js";
2
2
 
3
3
  //#region src/languages.ts
4
4
  const TYPES = [