@caddis/cli 0.4.39 → 0.4.42

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.js CHANGED
@@ -1209,7 +1209,7 @@ var require_command = __commonJS({
1209
1209
  "use strict";
1210
1210
  var EventEmitter = __require("events").EventEmitter;
1211
1211
  var childProcess = __require("child_process");
1212
- var path5 = __require("path");
1212
+ var path6 = __require("path");
1213
1213
  var fs = __require("fs");
1214
1214
  var process3 = __require("process");
1215
1215
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2222,9 +2222,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2222
2222
  let launchWithNode = false;
2223
2223
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2224
2224
  function findFile(baseDir, baseName) {
2225
- const localBin = path5.resolve(baseDir, baseName);
2225
+ const localBin = path6.resolve(baseDir, baseName);
2226
2226
  if (fs.existsSync(localBin)) return localBin;
2227
- if (sourceExt.includes(path5.extname(baseName))) return void 0;
2227
+ if (sourceExt.includes(path6.extname(baseName))) return void 0;
2228
2228
  const foundExt = sourceExt.find(
2229
2229
  (ext) => fs.existsSync(`${localBin}${ext}`)
2230
2230
  );
@@ -2242,17 +2242,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2242
2242
  } catch {
2243
2243
  resolvedScriptPath = this._scriptPath;
2244
2244
  }
2245
- executableDir = path5.resolve(
2246
- path5.dirname(resolvedScriptPath),
2245
+ executableDir = path6.resolve(
2246
+ path6.dirname(resolvedScriptPath),
2247
2247
  executableDir
2248
2248
  );
2249
2249
  }
2250
2250
  if (executableDir) {
2251
2251
  let localFile = findFile(executableDir, executableFile);
2252
2252
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2253
- const legacyName = path5.basename(
2253
+ const legacyName = path6.basename(
2254
2254
  this._scriptPath,
2255
- path5.extname(this._scriptPath)
2255
+ path6.extname(this._scriptPath)
2256
2256
  );
2257
2257
  if (legacyName !== this._name) {
2258
2258
  localFile = findFile(
@@ -2263,7 +2263,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2263
2263
  }
2264
2264
  executableFile = localFile || executableFile;
2265
2265
  }
2266
- launchWithNode = sourceExt.includes(path5.extname(executableFile));
2266
+ launchWithNode = sourceExt.includes(path6.extname(executableFile));
2267
2267
  let proc;
2268
2268
  if (process3.platform !== "win32") {
2269
2269
  if (launchWithNode) {
@@ -3178,7 +3178,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3178
3178
  * @return {Command}
3179
3179
  */
3180
3180
  nameFromFilename(filename) {
3181
- this._name = path5.basename(filename, path5.extname(filename));
3181
+ this._name = path6.basename(filename, path6.extname(filename));
3182
3182
  return this;
3183
3183
  }
3184
3184
  /**
@@ -3192,9 +3192,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3192
3192
  * @param {string} [path]
3193
3193
  * @return {(string|null|Command)}
3194
3194
  */
3195
- executableDir(path6) {
3196
- if (path6 === void 0) return this._executableDir;
3197
- this._executableDir = path6;
3195
+ executableDir(path7) {
3196
+ if (path7 === void 0) return this._executableDir;
3197
+ this._executableDir = path7;
3198
3198
  return this;
3199
3199
  }
3200
3200
  /**
@@ -3731,6 +3731,23 @@ var require_lib = __commonJS({
3731
3731
  }
3732
3732
  });
3733
3733
 
3734
+ // src/util/which.ts
3735
+ async function findBin(name) {
3736
+ try {
3737
+ const found = await (0, import_which.default)(name, { nothrow: true });
3738
+ return found ?? null;
3739
+ } catch {
3740
+ return null;
3741
+ }
3742
+ }
3743
+ var import_which;
3744
+ var init_which = __esm({
3745
+ "src/util/which.ts"() {
3746
+ "use strict";
3747
+ import_which = __toESM(require_lib(), 1);
3748
+ }
3749
+ });
3750
+
3734
3751
  // src/util/pkg.ts
3735
3752
  import { readFileSync, existsSync } from "fs";
3736
3753
  import { fileURLToPath } from "url";
@@ -4843,7 +4860,7 @@ var init_init = __esm({
4843
4860
  });
4844
4861
 
4845
4862
  // src/providers/resolve.ts
4846
- import { readFileSync as readFileSync4 } from "fs";
4863
+ import { readFileSync as readFileSync3 } from "fs";
4847
4864
  import { homedir } from "os";
4848
4865
  import { isAbsolute, join } from "path";
4849
4866
  function expandHome(p2) {
@@ -4856,10 +4873,10 @@ function keysFilePath(env) {
4856
4873
  const expanded = expandHome(configured);
4857
4874
  return isAbsolute(expanded) ? expanded : expanded;
4858
4875
  }
4859
- function parseKeysFile(path5) {
4876
+ function parseKeysFile(path6) {
4860
4877
  let text;
4861
4878
  try {
4862
- text = readFileSync4(path5, "utf8");
4879
+ text = readFileSync3(path6, "utf8");
4863
4880
  } catch {
4864
4881
  return {};
4865
4882
  }
@@ -4917,7 +4934,7 @@ __export(keys_exports, {
4917
4934
  probeKey: () => probeKey,
4918
4935
  upsertKeyLines: () => upsertKeyLines
4919
4936
  });
4920
- import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
4937
+ import { chmodSync, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
4921
4938
  import { dirname } from "path";
4922
4939
  async function probeKey(baseUrl, model, apiKey) {
4923
4940
  if (!baseUrl) return { ok: false, rejected: false, detail: "no endpoint for this provider" };
@@ -4971,18 +4988,18 @@ function upsertKeyLines(existing, updates) {
4971
4988
  if (!text.endsWith("\n")) text += "\n";
4972
4989
  return text;
4973
4990
  }
4974
- function restrictPermissions(path5) {
4991
+ function restrictPermissions(path6) {
4975
4992
  if (process.platform === "win32") return null;
4976
4993
  try {
4977
- chmodSync(path5, 384);
4994
+ chmodSync(path6, 384);
4978
4995
  return null;
4979
4996
  } catch (error) {
4980
4997
  return error instanceof Error ? error.message : String(error);
4981
4998
  }
4982
4999
  }
4983
5000
  async function keys(options = {}) {
4984
- const path5 = keysFilePath(process.env);
4985
- const fileKeys = parseKeysFile(path5);
5001
+ const path6 = keysFilePath(process.env);
5002
+ const fileKeys = parseKeysFile(path6);
4986
5003
  const names = Object.keys(PROVIDERS).sort();
4987
5004
  const current = names.map((name) => {
4988
5005
  const preset = PROVIDERS[name];
@@ -4998,7 +5015,7 @@ async function keys(options = {}) {
4998
5015
  if (options.check) {
4999
5016
  const configured = current.filter((entry) => entry.value);
5000
5017
  if (!configured.length) {
5001
- process.stdout.write(`no provider keys configured (looked in $ENV and ${path5})
5018
+ process.stdout.write(`no provider keys configured (looked in $ENV and ${path6})
5002
5019
  `);
5003
5020
  return 1;
5004
5021
  }
@@ -5013,7 +5030,7 @@ async function keys(options = {}) {
5013
5030
  return bad ? 1 : 0;
5014
5031
  }
5015
5032
  Ie("caddis keys");
5016
- process.stdout.write(` keys file: ${path5}
5033
+ process.stdout.write(` keys file: ${path6}
5017
5034
 
5018
5035
  `);
5019
5036
  for (const entry of current) {
@@ -5073,20 +5090,20 @@ async function keys(options = {}) {
5073
5090
  return 0;
5074
5091
  }
5075
5092
  if (options.dryRun) {
5076
- Se(`dry run \u2014 would write ${Object.keys(collected).join(", ")} to ${path5}`);
5093
+ Se(`dry run \u2014 would write ${Object.keys(collected).join(", ")} to ${path6}`);
5077
5094
  return 0;
5078
5095
  }
5079
5096
  let existing = "";
5080
5097
  try {
5081
- existing = readFileSync5(path5, "utf8");
5098
+ existing = readFileSync4(path6, "utf8");
5082
5099
  } catch {
5083
5100
  existing = "";
5084
5101
  }
5085
- mkdirSync2(dirname(path5), { recursive: true });
5086
- writeFileSync2(path5, upsertKeyLines(existing, collected), { encoding: "utf8" });
5087
- const permWarning = restrictPermissions(path5);
5102
+ mkdirSync(dirname(path6), { recursive: true });
5103
+ writeFileSync(path6, upsertKeyLines(existing, collected), { encoding: "utf8" });
5104
+ const permWarning = restrictPermissions(path6);
5088
5105
  process.stdout.write(`
5089
- wrote ${Object.keys(collected).join(", ")} to ${path5}
5106
+ wrote ${Object.keys(collected).join(", ")} to ${path6}
5090
5107
  `);
5091
5108
  if (permWarning) process.stdout.write(color.yellow(` could not set 0600 on the keys file: ${permWarning}
5092
5109
  `));
@@ -5160,18 +5177,22 @@ var doctor_exports = {};
5160
5177
  __export(doctor_exports, {
5161
5178
  doctor: () => doctor
5162
5179
  });
5180
+ import { existsSync as existsSync5 } from "fs";
5163
5181
  import os3 from "os";
5164
5182
  import process2 from "process";
5183
+ import path5 from "path";
5165
5184
  async function doctor(options) {
5166
5185
  const report = await gather(options.adapters, { checkCliUpdate: true });
5167
5186
  const findings = [];
5168
5187
  if (options.json) {
5169
- line(JSON.stringify(toJson(report), null, 2));
5188
+ const project = await renderProject(findings, { quiet: true });
5189
+ line(JSON.stringify(toJson(report, project), null, 2));
5170
5190
  return 0;
5171
5191
  }
5172
5192
  renderBanner({ cliVersion: report.cliVersion, poolVersion: report.poolVersion });
5173
5193
  renderEnvironment(report, findings);
5174
5194
  renderAgents(report, findings);
5195
+ await renderProject(findings);
5175
5196
  renderSummary(report, findings);
5176
5197
  return options.strict && findings.some((f) => f.kind === "problem") ? 1 : 0;
5177
5198
  }
@@ -5236,6 +5257,57 @@ function renderAgents(report, problems) {
5236
5257
  }
5237
5258
  }
5238
5259
  }
5260
+ async function renderProject(problems, options) {
5261
+ const quiet = options?.quiet ?? false;
5262
+ const python = await findBin("python");
5263
+ if (!python) {
5264
+ if (!quiet) {
5265
+ heading("Project");
5266
+ item("fail", "python not on PATH");
5267
+ }
5268
+ problems.push({
5269
+ kind: "problem",
5270
+ text: "python not on PATH",
5271
+ fix: "install Python 3.11 or later (see docs/ONBOARDING.md)"
5272
+ });
5273
+ return { ran: false, exitCode: null };
5274
+ }
5275
+ const bundle = bundlePath("antigravity-plugin", { requireManifest: false });
5276
+ const script = bundle ? path5.join(bundle, "scripts", "claudster_doctor.py") : null;
5277
+ if (!script || !existsSync5(script)) {
5278
+ if (!quiet) {
5279
+ heading("Project");
5280
+ item("fail", "claudster_doctor.py missing");
5281
+ }
5282
+ problems.push({
5283
+ kind: "problem",
5284
+ text: "claudster_doctor.py missing from antigravity-plugin bundle",
5285
+ fix: "reinstall: npm i -g @caddis/cli"
5286
+ });
5287
+ return { ran: false, exitCode: null };
5288
+ }
5289
+ if (!quiet) {
5290
+ heading("Project");
5291
+ }
5292
+ const result = await run(python, [script, "--dest", process2.cwd()], { timeout: 6e4 });
5293
+ if (!quiet && result.stdout) {
5294
+ const lines = result.stdout.replace(/\r\n/g, "\n").split("\n");
5295
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
5296
+ lines.pop();
5297
+ }
5298
+ for (const l2 of lines) {
5299
+ line(l2 ? ` ${l2}` : "");
5300
+ }
5301
+ }
5302
+ if (result.code === 1 || !result.ok) {
5303
+ problems.push({
5304
+ kind: "problem",
5305
+ text: "project health check failed",
5306
+ fix: "read the FAIL lines above"
5307
+ });
5308
+ }
5309
+ return { ran: true, exitCode: result.code };
5310
+ }
5239
5311
  function renderSummary(report, findings) {
5240
5312
  heading("Summary");
5241
5313
  const counts = report.agents.reduce(
@@ -5368,7 +5440,7 @@ function problemFor(entry, poolVersion) {
5368
5440
  return null;
5369
5441
  }
5370
5442
  }
5371
- function toJson(report) {
5443
+ function toJson(report, project) {
5372
5444
  return {
5373
5445
  cliVersion: report.cliVersion,
5374
5446
  cliUpdate: report.cliUpdate ?? null,
@@ -5389,7 +5461,8 @@ function toJson(report) {
5389
5461
  extrasVersion: entry.status.extras?.version ?? null,
5390
5462
  extrasDrift: entry.extrasDrift ?? null,
5391
5463
  note: entry.status.note ?? entry.detection.note ?? null
5392
- }))
5464
+ })),
5465
+ project: project ?? { ran: false, exitCode: null }
5393
5466
  };
5394
5467
  }
5395
5468
  var REQUIRED_NODE_MAJOR, REQUIRED_NODE_MINOR;
@@ -5397,7 +5470,9 @@ var init_doctor = __esm({
5397
5470
  "src/commands/doctor.ts"() {
5398
5471
  "use strict";
5399
5472
  init_log();
5473
+ init_exec();
5400
5474
  init_pkg();
5475
+ init_which();
5401
5476
  init_report();
5402
5477
  REQUIRED_NODE_MAJOR = 20;
5403
5478
  REQUIRED_NODE_MINOR = 19;
@@ -5544,19 +5619,7 @@ var {
5544
5619
 
5545
5620
  // src/agents/claude.ts
5546
5621
  init_exec();
5547
-
5548
- // src/util/which.ts
5549
- var import_which = __toESM(require_lib(), 1);
5550
- async function findBin(name) {
5551
- try {
5552
- const found = await (0, import_which.default)(name, { nothrow: true });
5553
- return found ?? null;
5554
- } catch {
5555
- return null;
5556
- }
5557
- }
5558
-
5559
- // src/agents/claude.ts
5622
+ init_which();
5560
5623
  var BIN = "claude";
5561
5624
  var MARKETPLACE = "caddis";
5562
5625
  var PLUGIN = "caddis@caddis";
@@ -5592,14 +5655,14 @@ function* pluginBlocks(stdout) {
5592
5655
  if (current) yield current;
5593
5656
  }
5594
5657
  async function detect() {
5595
- const path5 = await findBin(BIN);
5596
- if (!path5) {
5658
+ const path6 = await findBin(BIN);
5659
+ if (!path6) {
5597
5660
  return { present: false, note: "no `claude` binary on PATH" };
5598
5661
  }
5599
5662
  const version = await run(BIN, ["--version"], { timeout: 2e4 });
5600
5663
  return {
5601
5664
  present: true,
5602
- path: path5,
5665
+ path: path6,
5603
5666
  agentVersion: version.ok ? version.stdout.trim().split(/\s+/)[0] || void 0 : void 0,
5604
5667
  note: version.ok ? void 0 : "binary found but `claude --version` failed"
5605
5668
  };
@@ -5630,6 +5693,7 @@ async function status() {
5630
5693
  function steps(action) {
5631
5694
  if (action === "install") {
5632
5695
  return [
5696
+ { cmd: BIN, args: ["plugin", "marketplace", "add", "saajunaid/caddis-plugin"] },
5633
5697
  { cmd: BIN, args: ["plugin", "marketplace", "update", MARKETPLACE] },
5634
5698
  { cmd: BIN, args: ["plugin", "install", PLUGIN] }
5635
5699
  ];
@@ -5657,13 +5721,25 @@ async function drive(action, options) {
5657
5721
  for (const step of planned) {
5658
5722
  const command = formatCommand(step.cmd, step.args);
5659
5723
  const result = await run(step.cmd, step.args);
5724
+ const isMarketplaceAdd = step.args[1] === "marketplace" && step.args[2] === "add";
5725
+ const isMarketplaceUpdate = step.args[1] === "marketplace" && step.args[2] === "update";
5726
+ let ok = result.ok;
5727
+ let code = result.code;
5728
+ if (!ok && isMarketplaceAdd) {
5729
+ const output = `${result.stdout}
5730
+ ${result.stderr}`;
5731
+ if (/already (exists|added|configured|installed|on disk)/i.test(output)) {
5732
+ ok = true;
5733
+ code = 0;
5734
+ }
5735
+ }
5660
5736
  results.push({
5661
5737
  command,
5662
- ok: result.ok,
5663
- code: result.code,
5664
- output: result.ok ? void 0 : tail(result.stderr || result.stdout || result.failure || "")
5738
+ ok,
5739
+ code,
5740
+ output: ok ? void 0 : tail(result.stderr || result.stdout || result.failure || "")
5665
5741
  });
5666
- if (!result.ok && step.args[1] !== "marketplace") {
5742
+ if (!ok && !isMarketplaceUpdate) {
5667
5743
  return { ok: false, skipped: false, steps: results, message: `\`${command}\` failed` };
5668
5744
  }
5669
5745
  }
@@ -5684,10 +5760,11 @@ var claudeAdapter = {
5684
5760
 
5685
5761
  // src/agents/agy.ts
5686
5762
  init_exec();
5763
+ init_which();
5764
+ init_pkg();
5687
5765
  import { readFileSync as readFileSync2, existsSync as existsSync2, mkdtempSync, cpSync, rmSync } from "fs";
5688
5766
  import os from "os";
5689
5767
  import path2 from "path";
5690
- init_pkg();
5691
5768
  var BIN2 = "agy";
5692
5769
  var BUNDLE = "antigravity-plugin";
5693
5770
  var PLUGIN2 = "caddis";
@@ -5815,24 +5892,24 @@ async function drive2(_action, options) {
5815
5892
  }
5816
5893
  const cleanups = [];
5817
5894
  try {
5818
- const steps2 = [];
5895
+ const steps3 = [];
5819
5896
  for (const step of planned) {
5820
5897
  const command = formatCommand(step.cmd, step.args);
5821
5898
  const staged = stageForAgy(step.bundleDir);
5822
5899
  cleanups.push(staged.cleanup);
5823
5900
  const execArgs = [...step.args.slice(0, -1), staged.installPath];
5824
5901
  const result = await run(step.cmd, execArgs);
5825
- steps2.push({
5902
+ steps3.push({
5826
5903
  command,
5827
5904
  ok: result.ok,
5828
5905
  code: result.code,
5829
5906
  output: result.ok ? void 0 : (result.stderr || result.stdout || result.failure || "").trim().split(/\r?\n/).slice(-3).join("\n")
5830
5907
  });
5831
5908
  if (!result.ok) {
5832
- return { ok: false, skipped: false, steps: steps2, message: `\`${command}\` failed` };
5909
+ return { ok: false, skipped: false, steps: steps3, message: `\`${command}\` failed` };
5833
5910
  }
5834
5911
  }
5835
- return { ok: true, skipped: false, steps: steps2 };
5912
+ return { ok: true, skipped: false, steps: steps3 };
5836
5913
  } finally {
5837
5914
  cleanups.forEach((fn) => fn());
5838
5915
  }
@@ -5849,12 +5926,14 @@ var agyAdapter = {
5849
5926
 
5850
5927
  // src/agents/codex.ts
5851
5928
  init_exec();
5852
- import { cpSync as cpSync2, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync } from "fs";
5929
+ init_which();
5930
+ import { existsSync as existsSync3, rmSync as rmSync2 } from "fs";
5853
5931
  import os2 from "os";
5854
5932
  import path3 from "path";
5855
- init_pkg();
5856
5933
  var BIN3 = "codex";
5857
- var BUNDLE2 = "codex";
5934
+ var MARKETPLACE_URL = "https://github.com/saajunaid/caddis-plugin";
5935
+ var MARKETPLACE2 = "caddis";
5936
+ var PLUGIN3 = "caddis-codex@caddis";
5858
5937
  function caddisSkillsDir(home = os2.homedir()) {
5859
5938
  return path3.join(home, ".codex", "skills", "caddis");
5860
5939
  }
@@ -5874,7 +5953,6 @@ async function detect3() {
5874
5953
  return {
5875
5954
  present: true,
5876
5955
  path: binPath,
5877
- // `codex --version` prints "codex-cli 0.150.1"; take the trailing token.
5878
5956
  agentVersion: version.ok ? version.stdout.trim().split(/\s+/).pop() || void 0 : void 0,
5879
5957
  note: version.ok ? void 0 : "binary found but `codex --version` failed"
5880
5958
  };
@@ -5883,104 +5961,146 @@ function shortenHome2(target) {
5883
5961
  const home = os2.homedir();
5884
5962
  return target.startsWith(home) ? path3.join("~", target.slice(home.length)) : target;
5885
5963
  }
5886
- function statusFromHome2(home) {
5887
- const marker = versionFile(home);
5888
- const dir = caddisSkillsDir(home);
5889
- if (existsSync3(marker)) {
5890
- try {
5891
- const version = readFileSync3(marker, "utf8").trim();
5892
- if (version) return { installed: true, version, source: shortenHome2(marker) };
5893
- } catch {
5964
+ function parseCodexPluginList(stdout, plugin = PLUGIN3) {
5965
+ const wanted = plugin.toLowerCase();
5966
+ for (const rawLine of stdout.split(/\r?\n/)) {
5967
+ const line2 = rawLine.trim();
5968
+ if (!line2.toLowerCase().startsWith(wanted)) continue;
5969
+ const rest = line2.slice(wanted.length).trim();
5970
+ if (/^not installed/i.test(rest)) {
5971
+ return { installed: false };
5972
+ }
5973
+ const match = rest.match(/^installed,\s*(enabled|disabled)(?:\s+([^\s]+))?/i);
5974
+ if (match) {
5975
+ return {
5976
+ installed: true,
5977
+ disabled: match[1]?.toLowerCase() === "disabled",
5978
+ version: match[2]
5979
+ };
5980
+ }
5981
+ const simpleMatch = rest.match(/^installed(?:\s+([^\s]+))?/i);
5982
+ if (simpleMatch) {
5983
+ return {
5984
+ installed: true,
5985
+ disabled: false,
5986
+ version: simpleMatch[1]
5987
+ };
5894
5988
  }
5895
- return { installed: true, source: shortenHome2(marker), note: "version marker present but unreadable" };
5896
- }
5897
- if (existsSync3(dir)) {
5898
- return {
5899
- installed: true,
5900
- source: shortenHome2(dir),
5901
- note: "skills present but no version marker \u2014 installed by hand, or by a caddis older than this one"
5902
- };
5903
5989
  }
5904
- return { installed: false, source: shortenHome2(dir), note: "caddis skills not installed into codex" };
5990
+ return null;
5905
5991
  }
5906
5992
  async function status3() {
5907
5993
  const detected = await detect3();
5908
5994
  if (!detected.present) return { installed: false, note: "agent not installed" };
5909
- return statusFromHome2(os2.homedir());
5995
+ const listed = await run(BIN3, ["plugin", "list"], { timeout: 6e4 });
5996
+ if (!listed.ok) {
5997
+ return {
5998
+ installed: false,
5999
+ source: "`codex plugin list`",
6000
+ note: `could not read plugin list (${listed.failure ?? `exit ${listed.code}`})`
6001
+ };
6002
+ }
6003
+ const entry = parseCodexPluginList(listed.stdout);
6004
+ if (!entry || !entry.installed) {
6005
+ return { installed: false, source: "`codex plugin list`", note: "caddis-codex plugin not installed" };
6006
+ }
6007
+ return {
6008
+ installed: true,
6009
+ version: entry.version,
6010
+ disabled: entry.disabled,
6011
+ source: "`codex plugin list`",
6012
+ note: entry.version ? void 0 : "installed but the version is not reported"
6013
+ };
6014
+ }
6015
+ function steps2(action) {
6016
+ if (action === "install") {
6017
+ return [
6018
+ { cmd: BIN3, args: ["plugin", "marketplace", "add", MARKETPLACE_URL] },
6019
+ { cmd: BIN3, args: ["plugin", "add", PLUGIN3] }
6020
+ ];
6021
+ }
6022
+ return [
6023
+ { cmd: BIN3, args: ["plugin", "marketplace", "upgrade", MARKETPLACE2] },
6024
+ { cmd: BIN3, args: ["plugin", "add", PLUGIN3] }
6025
+ ];
5910
6026
  }
5911
6027
  async function drive3(action, options) {
5912
6028
  const detected = await detect3();
5913
6029
  if (!detected.present) {
5914
6030
  return { ok: true, skipped: true, steps: [], message: "codex not installed \u2014 skipped" };
5915
6031
  }
5916
- const bundle = bundlePath(BUNDLE2, { requireManifest: false });
5917
- if (!bundle) {
5918
- return {
5919
- ok: false,
5920
- skipped: false,
5921
- steps: [],
5922
- message: `the ${BUNDLE2} bundle is missing from this package \u2014 reinstall @caddis/cli`
5923
- };
5924
- }
5925
- const source = path3.join(bundle, ".codex", "skills");
5926
- if (!existsSync3(source)) {
5927
- return {
5928
- ok: false,
5929
- skipped: false,
5930
- steps: [],
5931
- message: `the ${BUNDLE2} bundle has no .codex/skills directory \u2014 the export shape changed`
5932
- };
5933
- }
5934
6032
  const home = os2.homedir();
5935
- const dest = caddisSkillsDir(home);
5936
- const version = bundleManifest().poolVersion || "unknown";
5937
- const label = `copy ${BUNDLE2} skills -> ${shortenHome2(dest)}`;
5938
- const steps2 = [];
6033
+ const legacyDir = caddisSkillsDir(home);
6034
+ const legacyMarker = versionFile(home);
6035
+ const hasLegacyMarker = existsSync3(legacyMarker);
6036
+ const planned = steps2(action);
6037
+ const removalCommand = `remove legacy ${shortenHome2(legacyDir)}`;
5939
6038
  if (options.dryRun) {
5940
- return {
5941
- ok: true,
5942
- skipped: true,
5943
- steps: [{ command: label, ok: true, code: 0 }],
5944
- message: `dry run \u2014 would ${action} caddis ${version} into ${shortenHome2(dest)}`
5945
- };
6039
+ const plannedSteps = planned.map((step) => ({ command: formatCommand(step.cmd, step.args), ok: true, code: null }));
6040
+ if (hasLegacyMarker) {
6041
+ plannedSteps.push({ command: removalCommand, ok: true, code: null });
6042
+ }
6043
+ return { ok: true, skipped: true, steps: plannedSteps, message: "dry run \u2014 nothing executed" };
5946
6044
  }
5947
- try {
5948
- if (existsSync3(dest)) rmSync2(dest, { recursive: true, force: true });
5949
- mkdirSync(path3.dirname(dest), { recursive: true });
5950
- cpSync2(source, dest, { recursive: true });
5951
- writeFileSync(versionFile(home), `${version}
5952
- `, "utf8");
5953
- steps2.push({ command: label, ok: true, code: 0 });
5954
- } catch (error) {
5955
- const message = error instanceof Error ? error.message : String(error);
5956
- steps2.push({ command: label, ok: false, code: null, output: message });
5957
- return { ok: false, skipped: false, steps: steps2, message: `could not install into codex: ${message}` };
6045
+ const results = [];
6046
+ for (const step of planned) {
6047
+ const command = formatCommand(step.cmd, step.args);
6048
+ const result = await run(step.cmd, step.args);
6049
+ const isMarketplaceAdd = step.args[1] === "marketplace" && step.args[2] === "add";
6050
+ const isMarketplaceUpgrade = step.args[1] === "marketplace" && step.args[2] === "upgrade";
6051
+ let ok = result.ok;
6052
+ let code = result.code;
6053
+ if (!ok && isMarketplaceAdd) {
6054
+ const output = `${result.stdout}
6055
+ ${result.stderr}`;
6056
+ if (/already (exists|added|configured|installed)/i.test(output)) {
6057
+ ok = true;
6058
+ code = 0;
6059
+ }
6060
+ }
6061
+ results.push({
6062
+ command,
6063
+ ok,
6064
+ code,
6065
+ output: ok ? void 0 : tail2(result.stderr || result.stdout || result.failure || "")
6066
+ });
6067
+ if (!ok && !isMarketplaceUpgrade) {
6068
+ return { ok: false, skipped: false, steps: results, message: `\`${command}\` failed` };
6069
+ }
5958
6070
  }
5959
- const notes = [];
5960
- for (const asset of ["AGENTS.md", path3.join(".codex", "config.toml.example")]) {
5961
- const from = path3.join(bundle, asset);
5962
- if (!existsSync3(from)) continue;
5963
- const to = path3.join(dest, `caddis-${path3.basename(asset)}`);
6071
+ if (hasLegacyMarker) {
5964
6072
  try {
5965
- cpSync2(from, to);
5966
- notes.push(path3.basename(to));
5967
- } catch {
6073
+ rmSync2(legacyDir, { recursive: true, force: true });
6074
+ results.push({ command: removalCommand, ok: true, code: 0 });
6075
+ } catch (error) {
6076
+ const message = error instanceof Error ? error.message : String(error);
6077
+ results.push({ command: removalCommand, ok: false, code: null, output: message });
5968
6078
  }
5969
6079
  }
5970
- const extrasNote = options.extras ? " (--extras has no codex bundle yet; core only)" : "";
5971
- const referenceNote = notes.length ? ` Reference copies beside it: ${notes.join(", ")} \u2014 config.toml is NEVER merged for you.` : "";
6080
+ const notes = [];
6081
+ if (!hasLegacyMarker && existsSync3(legacyDir)) {
6082
+ notes.push(
6083
+ `${shortenHome2(legacyDir)} exists but caddis did not write it (no version marker), so it was left in place; Codex may list the same skills twice. Remove it by hand if you do not need it.`
6084
+ );
6085
+ }
6086
+ if (options.extras) {
6087
+ notes.push("--extras has no Codex plugin; core only");
6088
+ }
5972
6089
  return {
5973
6090
  ok: true,
5974
6091
  skipped: false,
5975
- steps: steps2,
5976
- message: `caddis ${version} ${action === "install" ? "installed" : "updated"} in ${shortenHome2(dest)}.${referenceNote}${extrasNote}`
6092
+ steps: results,
6093
+ ...notes.length ? { message: notes.join(" ") } : {}
5977
6094
  };
5978
6095
  }
6096
+ function tail2(text, lines = 3) {
6097
+ return text.trim().split(/\r?\n/).slice(-lines).join("\n");
6098
+ }
5979
6099
  var codexAdapter = {
5980
6100
  id: "codex",
5981
6101
  name: "Codex",
5982
6102
  supported: true,
5983
- summary: "copy the caddis skill pool into ~/.codex/skills/caddis/ (config.toml never merged)",
6103
+ summary: "install the caddis-codex plugin from the caddis marketplace (config.toml never merged)",
5984
6104
  detect: detect3,
5985
6105
  drive: drive3,
5986
6106
  status: status3
@@ -5990,6 +6110,7 @@ var codexAdapter = {
5990
6110
  import path4 from "path";
5991
6111
 
5992
6112
  // src/agents/stub.ts
6113
+ init_which();
5993
6114
  import { existsSync as existsSync4 } from "fs";
5994
6115
  var NOT_YET_SUPPORTED = "not yet supported (v0.2)";
5995
6116
  function createStubAdapter(spec) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caddis/cli",
3
- "version": "0.4.39",
3
+ "version": "0.4.42",
4
4
  "description": "Install, update and diagnose the caddis dev harness across every coding agent you use (Claude Code, agy/Antigravity, and more).",
5
5
  "keywords": [
6
6
  "caddis",