coding-friend-cli 1.40.1 → 1.40.3

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.
Files changed (29) hide show
  1. package/dist/{chunk-B44ROTV6.js → chunk-2M42TEZC.js} +4 -4
  2. package/dist/{chunk-CZW36H3D.js → chunk-BQ4E66ZV.js} +1 -1
  3. package/dist/{chunk-E4NBTQXR.js → chunk-CGZNFAOB.js} +3 -2
  4. package/dist/{chunk-Q2QDSYQT.js → chunk-FK4SXIF7.js} +1 -1
  5. package/dist/{chunk-KTUPGMIV.js → chunk-FSQCAHWF.js} +35 -9
  6. package/dist/{chunk-TFF47F7P.js → chunk-HEVK3OYL.js} +1 -1
  7. package/dist/{chunk-3WIZA5OZ.js → chunk-ITI7MXLA.js} +2 -0
  8. package/dist/{chunk-KG6HYBGD.js → chunk-PIXAQLXQ.js} +43 -6
  9. package/dist/{chunk-Z5XTSF7L.js → chunk-XLB2UAKL.js} +40 -2
  10. package/dist/{clean-PKSSS662.js → clean-S57BNTWM.js} +2 -2
  11. package/dist/{config-WHEUK2LC.js → config-OFGL545F.js} +74 -13
  12. package/dist/{dev-HWKNWOZY.js → dev-GCVXE5TW.js} +7 -19
  13. package/dist/{disable-DCGVIHKG.js → disable-CN6GAG4I.js} +1 -1
  14. package/dist/{enable-YYWJDOYB.js → enable-MRLKTJRB.js} +1 -1
  15. package/dist/{host-GZJSMJSF.js → host-7XKH5HLB.js} +2 -2
  16. package/dist/index.js +34 -34
  17. package/dist/{init-TUBWQCBQ.js → init-XAWQUS2X.js} +364 -269
  18. package/dist/{install-45O5ICBA.js → install-B3646W7P.js} +5 -5
  19. package/dist/{learn-AJT7Z6F7.js → learn-Q5YHKHQH.js} +2 -2
  20. package/dist/{mcp-JGBZMNWL.js → mcp-ZG56IYLD.js} +6 -6
  21. package/dist/{mcp-serve-IS4T24CV.js → mcp-serve-7RWXY3J4.js} +2 -2
  22. package/dist/{memory-4NVLWOFS.js → memory-LBVPVWJQ.js} +6 -6
  23. package/dist/{permission-DDBMBFJV.js → permission-W3NM7HOL.js} +3 -3
  24. package/dist/{session-GBHL5TH3.js → session-WU5344ZD.js} +2 -2
  25. package/dist/{status-KQQJNSEZ.js → status-GQ3ZDYZ4.js} +8 -8
  26. package/dist/{statusline-B7PXP3AQ.js → statusline-CO4ZOUXH.js} +2 -2
  27. package/dist/{uninstall-6QKNGLKK.js → uninstall-S5FSXBYX.js} +2 -2
  28. package/dist/{update-YJ363TBU.js → update-HGEF46T5.js} +7 -5
  29. package/package.json +1 -1
@@ -2,20 +2,20 @@ import {
2
2
  checkMemoryMcpHealth,
3
3
  printHealthSection,
4
4
  warnStaleMcpJson
5
- } from "./chunk-TFF47F7P.js";
5
+ } from "./chunk-HEVK3OYL.js";
6
6
  import {
7
7
  loadConfig,
8
8
  resolveMemoryDir
9
- } from "./chunk-E4NBTQXR.js";
9
+ } from "./chunk-CGZNFAOB.js";
10
10
  import {
11
11
  getMemoryMcpStatus,
12
12
  isMemoryMcpRegistered,
13
13
  memoryConfigMenu,
14
14
  registerMemoryMcp
15
- } from "./chunk-CZW36H3D.js";
15
+ } from "./chunk-BQ4E66ZV.js";
16
16
  import {
17
17
  showConfigHint
18
- } from "./chunk-Z5XTSF7L.js";
18
+ } from "./chunk-XLB2UAKL.js";
19
19
  import {
20
20
  getLibPath
21
21
  } from "./chunk-TN3Y75WG.js";
@@ -11,7 +11,7 @@ import {
11
11
  askScope,
12
12
  formatScopeLabel,
13
13
  injectBackChoice
14
- } from "./chunk-Z5XTSF7L.js";
14
+ } from "./chunk-XLB2UAKL.js";
15
15
  import {
16
16
  getLibPath
17
17
  } from "./chunk-TN3Y75WG.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_CONFIG
3
- } from "./chunk-3WIZA5OZ.js";
3
+ } from "./chunk-ITI7MXLA.js";
4
4
  import {
5
5
  readJson
6
6
  } from "./chunk-5UVDWG5L.js";
@@ -101,7 +101,8 @@ var MemoryConfigSchema = z.object({
101
101
  autoStart: z.boolean().optional()
102
102
  });
103
103
  var ReviewConfigSchema = z.strictObject({
104
- withCodex: z.boolean().optional()
104
+ withCodex: z.boolean().optional(),
105
+ maxRounds: z.number().int().min(1).optional()
105
106
  });
106
107
  var ConfigSchema = z.strictObject({
107
108
  language: z.string().optional(),
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ALL_COMPONENT_IDS,
3
3
  STATUSLINE_COMPONENTS
4
- } from "./chunk-3WIZA5OZ.js";
4
+ } from "./chunk-ITI7MXLA.js";
5
5
  import {
6
6
  mergeJson,
7
7
  readJson,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ensureStatusline,
3
3
  getInstalledVersion
4
- } from "./chunk-Q2QDSYQT.js";
4
+ } from "./chunk-FK4SXIF7.js";
5
5
  import {
6
6
  ensureShellCompletion
7
7
  } from "./chunk-667NMPN4.js";
@@ -9,7 +9,7 @@ import {
9
9
  isMemoryMcpRegistered,
10
10
  registerMemoryMcp,
11
11
  removeMemoryMcpEntry
12
- } from "./chunk-CZW36H3D.js";
12
+ } from "./chunk-BQ4E66ZV.js";
13
13
  import {
14
14
  isPluginInstalled
15
15
  } from "./chunk-SUYE5S2W.js";
@@ -33,7 +33,7 @@ import {
33
33
  import {
34
34
  resolveHostFlags,
35
35
  resolveScope
36
- } from "./chunk-Z5XTSF7L.js";
36
+ } from "./chunk-XLB2UAKL.js";
37
37
  import {
38
38
  detectHostsAvailable
39
39
  } from "./chunk-OBCNHQFA.js";
@@ -55,7 +55,7 @@ import {
55
55
  } from "./chunk-NREZK463.js";
56
56
 
57
57
  // src/commands/update.ts
58
- import { readFileSync } from "fs";
58
+ import { lstatSync, readFileSync } from "fs";
59
59
  import { dirname, join } from "path";
60
60
  import { fileURLToPath } from "url";
61
61
  import chalk from "chalk";
@@ -78,6 +78,31 @@ function getCliVersion() {
78
78
  function getLatestCliVersion() {
79
79
  return run("npm", ["view", "coding-friend-cli", "version"]);
80
80
  }
81
+ function shouldUpdateCli(requested) {
82
+ if (!requested) return false;
83
+ if (isCliGlobalLinked()) {
84
+ log.info(
85
+ "CLI is npm-linked to a local repo \u2014 skipping CLI update. Build it there instead (`npm run dev`)."
86
+ );
87
+ return false;
88
+ }
89
+ return true;
90
+ }
91
+ function isCliGlobalLinked() {
92
+ const prefix = run("npm", ["prefix", "-g"]);
93
+ if (!prefix) return false;
94
+ const modules = "node_modules";
95
+ return [
96
+ join(prefix.trim(), "lib", modules, "coding-friend-cli"),
97
+ join(prefix.trim(), modules, "coding-friend-cli")
98
+ ].some((p) => {
99
+ try {
100
+ return lstatSync(p).isSymbolicLink();
101
+ } catch {
102
+ return false;
103
+ }
104
+ });
105
+ }
81
106
  function getLatestVersion() {
82
107
  let tag = null;
83
108
  tag = run("gh", [
@@ -191,7 +216,6 @@ async function updateCommand(opts) {
191
216
  }
192
217
  async function updateClaudeCommand(opts, mode) {
193
218
  const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
194
- const doCli = (updateAll || !!opts.cli) && !mode?.skipCli;
195
219
  const doPlugin = updateAll || !!opts.plugin;
196
220
  const doStatusline = updateAll || !!opts.statusline;
197
221
  const hasScopeFlag = !!(opts.user || opts.global || opts.project || opts.local);
@@ -205,6 +229,7 @@ async function updateClaudeCommand(opts, mode) {
205
229
  printBanner("\u2728 Coding Friend Update \u2728");
206
230
  console.log();
207
231
  }
232
+ const doCli = shouldUpdateCli((updateAll || !!opts.cli) && !mode?.skipCli);
208
233
  const currentVersion = getInstalledVersion();
209
234
  const latestVersion = getLatestVersion();
210
235
  const statuslineVersion = getStatuslineVersion();
@@ -323,7 +348,7 @@ async function updateClaudeCommand(opts, mode) {
323
348
  }
324
349
  let refreshMemoryAfterUpdate = null;
325
350
  if (doCli) {
326
- ({ refreshMemoryAfterUpdate } = await import("./memory-4NVLWOFS.js"));
351
+ ({ refreshMemoryAfterUpdate } = await import("./memory-LBVPVWJQ.js"));
327
352
  if (!latestCliVersion) {
328
353
  log.warn("Cannot check latest CLI version from npm.");
329
354
  } else {
@@ -383,12 +408,12 @@ async function updateClaudeCommand(opts, mode) {
383
408
  }
384
409
  async function updateCodexCommand(opts, mode) {
385
410
  const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
386
- const doCli = (updateAll || !!opts.cli) && !mode?.skipCli;
387
411
  const doPlugin = updateAll || !!opts.plugin;
388
412
  if (!mode?.skipBanner) {
389
413
  printBanner("\u2728 Coding Friend Codex Update \u2728");
390
414
  console.log();
391
415
  }
416
+ const doCli = shouldUpdateCli((updateAll || !!opts.cli) && !mode?.skipCli);
392
417
  if (doPlugin) {
393
418
  const beforeVersion = getCodexInstalledVersion();
394
419
  console.log(
@@ -484,13 +509,13 @@ function ompUpdateScopes(opts) {
484
509
  }
485
510
  async function updateOmpCommand(opts, mode) {
486
511
  const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
487
- const doCli = (updateAll || !!opts.cli) && !mode?.skipCli;
488
512
  const doPlugin = updateAll || !!opts.plugin;
489
513
  const scopes = ompUpdateScopes(opts);
490
514
  if (!mode?.skipBanner) {
491
515
  printBanner("\u2728 Coding Friend omp Update \u2728");
492
516
  console.log();
493
517
  }
518
+ const doCli = shouldUpdateCli((updateAll || !!opts.cli) && !mode?.skipCli);
494
519
  if (doPlugin) {
495
520
  const cliVersion = getCliVersion();
496
521
  console.log(` CLI ${cliVersion}`);
@@ -554,12 +579,12 @@ function restoreAgyExtraMcpServers(previous) {
554
579
  }
555
580
  async function updateAgyCommand(opts, mode) {
556
581
  const updateAll = !opts.cli && !opts.plugin && !opts.statusline;
557
- const doCli = (updateAll || !!opts.cli) && !mode?.skipCli;
558
582
  const doPlugin = updateAll || !!opts.plugin;
559
583
  if (!mode?.skipBanner) {
560
584
  printBanner("\u2728 Coding Friend Antigravity Update (beta) \u2728");
561
585
  console.log();
562
586
  }
587
+ const doCli = shouldUpdateCli((updateAll || !!opts.cli) && !mode?.skipCli);
563
588
  if (doPlugin) {
564
589
  try {
565
590
  const previousMcp = readAgyMcpConfig();
@@ -617,6 +642,7 @@ export {
617
642
  semverCompare,
618
643
  getCliVersion,
619
644
  getLatestCliVersion,
645
+ isCliGlobalLinked,
620
646
  getLatestVersion,
621
647
  updateCommand
622
648
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isMemoryMcpRegistered
3
- } from "./chunk-CZW36H3D.js";
3
+ } from "./chunk-BQ4E66ZV.js";
4
4
  import {
5
5
  readJson
6
6
  } from "./chunk-5UVDWG5L.js";
@@ -23,6 +23,7 @@ var STATUSLINE_COMPONENTS = [
23
23
  }
24
24
  ];
25
25
  var ALL_COMPONENT_IDS = STATUSLINE_COMPONENTS.map((c) => c.id);
26
+ var DEFAULT_REVIEW_MAX_ROUNDS = 5;
26
27
  var DEFAULT_CONFIG = {
27
28
  language: "en",
28
29
  docsDir: "docs",
@@ -54,5 +55,6 @@ var DEFAULT_CONFIG = {
54
55
  export {
55
56
  STATUSLINE_COMPONENTS,
56
57
  ALL_COMPONENT_IDS,
58
+ DEFAULT_REVIEW_MAX_ROUNDS,
57
59
  DEFAULT_CONFIG
58
60
  };
@@ -6,10 +6,13 @@ import {
6
6
  claudeLocalSettingsPath,
7
7
  claudeProjectSettingsPath,
8
8
  claudeSettingsPath,
9
+ knownMarketplacesPath,
9
10
  pluginCachePath
10
11
  } from "./chunk-IYBIIRH4.js";
11
12
 
12
13
  // src/lib/permissions.ts
14
+ import { join } from "path";
15
+ var MARKETPLACE_NAME = "coding-friend-marketplace";
13
16
  var STATIC_RULES = [
14
17
  // Core Utilities (hooks & infrastructure)
15
18
  {
@@ -260,7 +263,14 @@ var STATIC_RULES = [
260
263
  recommended: true
261
264
  }
262
265
  ];
263
- function buildPluginScriptRules() {
266
+ function resolveMarketplacePluginRoot() {
267
+ const data = readJson(
268
+ knownMarketplacesPath()
269
+ );
270
+ const installLocation = data?.[MARKETPLACE_NAME]?.installLocation;
271
+ return installLocation ? join(installLocation, "plugin") : null;
272
+ }
273
+ function buildPluginScriptRules(marketplaceRoot = resolveMarketplacePluginRoot()) {
264
274
  const absBase = pluginCachePath();
265
275
  const readBase = process.env.CLAUDE_CONFIG_DIR?.trim() ? absBase : "~/.claude/plugins/cache/coding-friend-marketplace/coding-friend";
266
276
  return [
@@ -287,11 +297,35 @@ function buildPluginScriptRules() {
287
297
  description: "[read-only] Read user global config \xB7 Used by: session-init hook, /cf-learn",
288
298
  category: "Plugin Scripts",
289
299
  recommended: true
290
- }
300
+ },
301
+ // Skills and hooks self-reference via ${CLAUDE_PLUGIN_ROOT}, which points at
302
+ // the marketplace's install location — the repo itself in dev mode, the
303
+ // clone under ~/.claude/plugins/marketplaces/ otherwise. Neither is the
304
+ // versioned cache above, so those paths need their own rules.
305
+ ...marketplaceRoot ? [
306
+ {
307
+ rule: `Bash(bash ${marketplaceRoot}/*)`,
308
+ description: "[execute] Run plugin scripts from the marketplace root (unquoted) \xB7 Used by: all skills",
309
+ category: "Plugin Scripts",
310
+ recommended: true
311
+ },
312
+ {
313
+ rule: `Bash(bash "${marketplaceRoot}/*)`,
314
+ description: "[execute] Run plugin scripts from the marketplace root (quoted) \xB7 Used by: all skills",
315
+ category: "Plugin Scripts",
316
+ recommended: true
317
+ },
318
+ {
319
+ rule: `Read(${marketplaceRoot}/**)`,
320
+ description: "[read-only] Read plugin files from the marketplace root \xB7 Used by: hooks, agents",
321
+ category: "Plugin Scripts",
322
+ recommended: true
323
+ }
324
+ ] : []
291
325
  ];
292
326
  }
293
- function getAllRules() {
294
- return [...STATIC_RULES, ...buildPluginScriptRules()];
327
+ function getAllRules(marketplaceRoot) {
328
+ return [...STATIC_RULES, ...buildPluginScriptRules(marketplaceRoot)];
295
329
  }
296
330
  var PERMISSION_RULES = getAllRules();
297
331
  var DANGEROUS_RULE_PATTERNS = [
@@ -423,12 +457,15 @@ function extractTag(description) {
423
457
  const match = description.match(/^(\[[^\]]+\])/);
424
458
  return match ? match[1] : null;
425
459
  }
426
- var OLD_PLUGIN_PATH_PATTERN = "/.claude/plugins/cache/coding-friend-marketplace/coding-friend/";
460
+ var OLD_PLUGIN_PATH_PATTERNS = [
461
+ "/.claude/plugins/cache/coding-friend-marketplace/coding-friend/",
462
+ `/.claude/plugins/marketplaces/${MARKETPLACE_NAME}/plugin/`
463
+ ];
427
464
  function cleanupStalePluginRules(settingsPath) {
428
465
  const existing = getExistingRules(settingsPath);
429
466
  const currentRules = new Set(getAllRules().map((r) => r.rule));
430
467
  const stale = existing.filter(
431
- (r) => r.includes(OLD_PLUGIN_PATH_PATTERN) && !currentRules.has(r)
468
+ (r) => OLD_PLUGIN_PATH_PATTERNS.some((p) => r.includes(p)) && !currentRules.has(r)
432
469
  );
433
470
  if (stale.length === 0) return 0;
434
471
  applyPermissions(settingsPath, [], stale);
@@ -16,10 +16,42 @@ import {
16
16
  } from "./chunk-NREZK463.js";
17
17
 
18
18
  // src/lib/prompt-utils.ts
19
- import { select, Separator } from "@inquirer/prompts";
19
+ import { checkbox, confirm, input, select, Separator } from "@inquirer/prompts";
20
20
  import { existsSync } from "fs";
21
+ import { emitKeypressEvents } from "readline";
21
22
  import chalk from "chalk";
22
23
  var BACK = "__back__";
24
+ var BackError = class extends Error {
25
+ constructor() {
26
+ super("back");
27
+ this.name = "BackError";
28
+ }
29
+ };
30
+ var escArmed = false;
31
+ function setEscArmed(armed) {
32
+ escArmed = armed;
33
+ }
34
+ async function withEsc(run2) {
35
+ if (!escArmed) return run2();
36
+ const controller = new AbortController();
37
+ const onKey = (_str, key) => {
38
+ if (key?.name === "escape") controller.abort();
39
+ };
40
+ emitKeypressEvents(process.stdin);
41
+ process.stdin.on("keypress", onKey);
42
+ try {
43
+ return await run2({ signal: controller.signal });
44
+ } catch (err) {
45
+ if (err?.name === "AbortPromptError") throw new BackError();
46
+ throw err;
47
+ } finally {
48
+ process.stdin.off("keypress", onKey);
49
+ }
50
+ }
51
+ var escSelect = (config) => withEsc((ctx) => select(config, ctx));
52
+ var escInput = (config) => withEsc((ctx) => input(config, ctx));
53
+ var escConfirm = (config) => withEsc((ctx) => confirm(config, ctx));
54
+ var escCheckbox = (config) => withEsc((ctx) => checkbox(config, ctx));
23
55
  function injectBackChoice(choices, label = "Back") {
24
56
  return [
25
57
  ...choices,
@@ -28,7 +60,7 @@ function injectBackChoice(choices, label = "Back") {
28
60
  ];
29
61
  }
30
62
  async function askScope(label = "Save to:") {
31
- return select({
63
+ return escSelect({
32
64
  message: label,
33
65
  choices: [
34
66
  { name: "This project only", value: "local" },
@@ -185,6 +217,12 @@ function applyDocsDirChange(newValue, oldValue, scope, subfolders = []) {
185
217
 
186
218
  export {
187
219
  BACK,
220
+ BackError,
221
+ setEscArmed,
222
+ escSelect,
223
+ escInput,
224
+ escConfirm,
225
+ escCheckbox,
188
226
  injectBackChoice,
189
227
  askScope,
190
228
  resolveScope,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  loadConfig,
3
3
  resolveMemoryDir
4
- } from "./chunk-E4NBTQXR.js";
5
- import "./chunk-3WIZA5OZ.js";
4
+ } from "./chunk-CGZNFAOB.js";
5
+ import "./chunk-ITI7MXLA.js";
6
6
  import "./chunk-5UVDWG5L.js";
7
7
  import "./chunk-EVGXUDX4.js";
8
8
  import {
@@ -2,13 +2,13 @@ import {
2
2
  afterAutoApproveEnabled,
3
3
  getAllRules,
4
4
  getExistingRules
5
- } from "./chunk-KG6HYBGD.js";
5
+ } from "./chunk-PIXAQLXQ.js";
6
6
  import {
7
7
  listMdFilesRecursive
8
8
  } from "./chunk-2DTXVMPQ.js";
9
9
  import {
10
10
  resolveLearnDir
11
- } from "./chunk-E4NBTQXR.js";
11
+ } from "./chunk-CGZNFAOB.js";
12
12
  import {
13
13
  findStatuslineHookPath,
14
14
  getCurrentAccountEmail,
@@ -19,11 +19,12 @@ import {
19
19
  saveStatuslineConfig,
20
20
  selectStatuslineComponents,
21
21
  writeStatuslineSettings
22
- } from "./chunk-Q2QDSYQT.js";
22
+ } from "./chunk-FK4SXIF7.js";
23
23
  import {
24
24
  ALL_COMPONENT_IDS,
25
- DEFAULT_CONFIG
26
- } from "./chunk-3WIZA5OZ.js";
25
+ DEFAULT_CONFIG,
26
+ DEFAULT_REVIEW_MAX_ROUNDS
27
+ } from "./chunk-ITI7MXLA.js";
27
28
  import {
28
29
  registerLearnMcp,
29
30
  unregisterLearnMcp
@@ -35,7 +36,7 @@ import {
35
36
  } from "./chunk-667NMPN4.js";
36
37
  import {
37
38
  memoryConfigMenu
38
- } from "./chunk-CZW36H3D.js";
39
+ } from "./chunk-BQ4E66ZV.js";
39
40
  import "./chunk-KCBMCHSA.js";
40
41
  import {
41
42
  BACK,
@@ -46,7 +47,7 @@ import {
46
47
  getScopeLabel,
47
48
  injectBackChoice,
48
49
  showConfigHint
49
- } from "./chunk-Z5XTSF7L.js";
50
+ } from "./chunk-XLB2UAKL.js";
50
51
  import "./chunk-TN3Y75WG.js";
51
52
  import "./chunk-OBCNHQFA.js";
52
53
  import {
@@ -516,8 +517,14 @@ async function editTdd(globalCfg, localCfg) {
516
517
  if (scope === "back") return;
517
518
  writeToScope(scope, { tdd: value });
518
519
  }
520
+ function getReviewConfig(globalCfg, localCfg) {
521
+ return {
522
+ ...globalCfg?.review ?? {},
523
+ ...localCfg?.review ?? {}
524
+ };
525
+ }
519
526
  async function editReviewWithCodex(globalCfg, localCfg) {
520
- const currentValue = getMergedValue("review", globalCfg, localCfg)?.withCodex;
527
+ const currentValue = getReviewConfig(globalCfg, localCfg).withCodex;
521
528
  if (currentValue !== void 0) {
522
529
  log.dim(`Current: ${currentValue}`);
523
530
  }
@@ -530,6 +537,59 @@ async function editReviewWithCodex(globalCfg, localCfg) {
530
537
  if (scope === "back") return;
531
538
  writeNestedField("review", scope, "withCodex", value);
532
539
  }
540
+ async function editReviewMaxRounds(globalCfg, localCfg) {
541
+ const currentValue = getReviewConfig(globalCfg, localCfg).maxRounds;
542
+ if (currentValue !== void 0) {
543
+ log.dim(`Current: ${currentValue}`);
544
+ }
545
+ log.dim(
546
+ "Caps how many /cf-review rounds /cf-plan --auto and /cf-tdd --auto run before waiting for you (initial review + fix re-reviews)."
547
+ );
548
+ const raw = await input({
549
+ message: "Maximum review rounds:",
550
+ default: String(currentValue ?? DEFAULT_REVIEW_MAX_ROUNDS),
551
+ validate: (val) => {
552
+ const n = Number(val);
553
+ if (!Number.isInteger(n) || n < 1) return "Must be an integer \u2265 1";
554
+ return true;
555
+ }
556
+ });
557
+ const scope = await askScope();
558
+ if (scope === "back") return;
559
+ writeNestedField("review", scope, "maxRounds", Number(raw));
560
+ }
561
+ async function reviewSubMenu() {
562
+ while (true) {
563
+ const globalCfg = readJson(globalConfigPath());
564
+ const localCfg = readJson(localConfigPath());
565
+ const review = getReviewConfig(globalCfg, localCfg);
566
+ const choice = await select({
567
+ message: "Review settings:",
568
+ choices: injectBackChoice(
569
+ [
570
+ {
571
+ name: `Codex dual-review (${review.withCodex ?? false})`,
572
+ value: "withCodex"
573
+ },
574
+ {
575
+ name: `Max review rounds (${review.maxRounds ?? DEFAULT_REVIEW_MAX_ROUNDS})`,
576
+ value: "maxRounds"
577
+ }
578
+ ],
579
+ "Back"
580
+ )
581
+ });
582
+ if (choice === BACK) return;
583
+ switch (choice) {
584
+ case "withCodex":
585
+ await editReviewWithCodex(globalCfg, localCfg);
586
+ break;
587
+ case "maxRounds":
588
+ await editReviewMaxRounds(globalCfg, localCfg);
589
+ break;
590
+ }
591
+ }
592
+ }
533
593
  async function editAutoApprove(globalCfg, localCfg) {
534
594
  const currentValue = getMergedValue("autoApprove", globalCfg, localCfg);
535
595
  if (currentValue !== void 0) {
@@ -742,7 +802,7 @@ async function editPermissions() {
742
802
  )
743
803
  });
744
804
  if (choice === BACK) return;
745
- const { permissionCommand } = await import("./permission-DDBMBFJV.js");
805
+ const { permissionCommand } = await import("./permission-W3NM7HOL.js");
746
806
  switch (choice) {
747
807
  case "interactive":
748
808
  await permissionCommand({});
@@ -788,7 +848,8 @@ async function configCommand() {
788
848
  localCfg
789
849
  );
790
850
  const reviewScope = getScopeLabel("review", globalCfg, localCfg);
791
- const withCodexVal = getMergedValue("review", globalCfg, localCfg)?.withCodex;
851
+ const reviewVal = getReviewConfig(globalCfg, localCfg);
852
+ const maxRoundsVal = reviewVal.maxRounds ?? DEFAULT_REVIEW_MAX_ROUNDS;
792
853
  const planDocsScope = getScopeLabel("disableGUIPlan", globalCfg, localCfg);
793
854
  const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
794
855
  const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
@@ -841,9 +902,9 @@ async function configCommand() {
841
902
  description: " Block reads of .env, keys, and similar secrets (all hosts)"
842
903
  },
843
904
  {
844
- name: `Codex dual-review ${formatScopeLabel(reviewScope)}${withCodexVal !== void 0 ? ` (${withCodexVal})` : ""}`,
905
+ name: `Review settings ${formatScopeLabel(reviewScope)} (${maxRoundsVal} rounds)`,
845
906
  value: "review",
846
- description: " Run a Codex second-opinion review alongside Claude's on every /cf-review"
907
+ description: " Autopilot review-fix rounds + Codex second-opinion on every /cf-review"
847
908
  },
848
909
  {
849
910
  name: `Plan docs ${formatScopeLabel(planDocsScope)}`,
@@ -900,7 +961,7 @@ async function configCommand() {
900
961
  await editPrivacyBlock(globalCfg, localCfg);
901
962
  break;
902
963
  case "review":
903
- await editReviewWithCodex(globalCfg, localCfg);
964
+ await reviewSubMenu();
904
965
  break;
905
966
  case "planDocs":
906
967
  await planDocsSubMenu();
@@ -1,7 +1,8 @@
1
1
  import {
2
- ensureStatusline
3
- } from "./chunk-Q2QDSYQT.js";
4
- import "./chunk-3WIZA5OZ.js";
2
+ ensureStatusline,
3
+ getInstalledVersion
4
+ } from "./chunk-FK4SXIF7.js";
5
+ import "./chunk-ITI7MXLA.js";
5
6
  import {
6
7
  ensureShellCompletion
7
8
  } from "./chunk-667NMPN4.js";
@@ -242,22 +243,9 @@ async function devSyncCommand() {
242
243
  );
243
244
  return;
244
245
  }
245
- const cacheBase = pluginCachePath();
246
- let cacheVersionDir = null;
247
- if (existsSync(cacheBase)) {
248
- const versions = readdirSync(cacheBase).filter(
249
- (v) => statSync(join(cacheBase, v)).isDirectory()
250
- );
251
- if (versions.length > 0) {
252
- cacheVersionDir = join(
253
- cacheBase,
254
- versions.sort((a, b) => {
255
- return statSync(join(cacheBase, b)).mtimeMs - statSync(join(cacheBase, a)).mtimeMs;
256
- })[0]
257
- );
258
- }
259
- }
260
- if (!cacheVersionDir) {
246
+ const installedVersion = getInstalledVersion();
247
+ const cacheVersionDir = installedVersion ? join(pluginCachePath(), installedVersion) : null;
248
+ if (!cacheVersionDir || !existsSync(cacheVersionDir)) {
261
249
  log.error(
262
250
  "No cached plugin version found. Run `cf dev off && cf dev on` first."
263
251
  );
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  resolveHostFlags,
16
16
  resolveScope
17
- } from "./chunk-Z5XTSF7L.js";
17
+ } from "./chunk-XLB2UAKL.js";
18
18
  import "./chunk-TN3Y75WG.js";
19
19
  import "./chunk-OBCNHQFA.js";
20
20
  import "./chunk-5UVDWG5L.js";
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  resolveHostFlags,
16
16
  resolveScope
17
- } from "./chunk-Z5XTSF7L.js";
17
+ } from "./chunk-XLB2UAKL.js";
18
18
  import "./chunk-TN3Y75WG.js";
19
19
  import "./chunk-OBCNHQFA.js";
20
20
  import "./chunk-5UVDWG5L.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  resolveLearnDir
3
- } from "./chunk-E4NBTQXR.js";
4
- import "./chunk-3WIZA5OZ.js";
3
+ } from "./chunk-CGZNFAOB.js";
4
+ import "./chunk-ITI7MXLA.js";
5
5
  import {
6
6
  getLibPath
7
7
  } from "./chunk-TN3Y75WG.js";