coding-friend-cli 1.34.0 → 1.35.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.
Files changed (36) hide show
  1. package/README.md +1 -0
  2. package/dist/chunk-2DTXVMPQ.js +26 -0
  3. package/dist/{chunk-OYPOADCX.js → chunk-57EGAXYI.js} +3 -3
  4. package/dist/{chunk-K6EDQGDN.js → chunk-B2NFXRFP.js} +17 -12
  5. package/dist/{chunk-3UIH5U3Y.js → chunk-C5YWESSF.js} +2 -6
  6. package/dist/chunk-E5J24GD5.js +190 -0
  7. package/dist/{chunk-UGTNIFBH.js → chunk-H5TXJZD7.js} +6 -6
  8. package/dist/{chunk-5X3JZKYW.js → chunk-L53RNEV3.js} +116 -73
  9. package/dist/{chunk-DMJCO6LJ.js → chunk-RLUNNJWJ.js} +2 -1
  10. package/dist/{chunk-XOHLBLN2.js → chunk-WNTMKIKC.js} +1 -1
  11. package/dist/chunk-WSUYLIYX.js +61 -0
  12. package/dist/clean-67ACB4OS.js +95 -0
  13. package/dist/{chunk-CXG6I6ZF.js → config-HTQAALMA.js} +182 -194
  14. package/dist/{dev-S6VZUD33.js → dev-PFUF67SH.js} +7 -7
  15. package/dist/{disable-ZWQVU3QZ.js → disable-CDOM7SSI.js} +2 -2
  16. package/dist/{enable-NXP6APNI.js → enable-SF7742UR.js} +2 -2
  17. package/dist/{host-Y3WIXDSS.js → host-HKA4OHBM.js} +3 -3
  18. package/dist/index.js +38 -34
  19. package/dist/{init-WDFYY5MM.js → init-YM7YEEGK.js} +154 -99
  20. package/dist/{install-FKL6WLIG.js → install-VYZSTXN6.js} +8 -8
  21. package/dist/{mcp-J4SD646S.js → mcp-7MO2MCLE.js} +72 -67
  22. package/dist/{mcp-serve-learn-UNW6VH5M.js → mcp-serve-learn-OKVC5WV3.js} +19 -3
  23. package/dist/{memory-RLZR7XQY.js → memory-XALEMWQF.js} +7 -7
  24. package/dist/{permission-DNW7DE24.js → permission-C5R3LMZQ.js} +2 -2
  25. package/dist/postinstall.js +1 -1
  26. package/dist/{session-6WQGKIRG.js → session-M4KRQ4VU.js} +2 -2
  27. package/dist/{status-BX2SVBBW.js → status-2XM5OZDR.js} +19 -14
  28. package/dist/{statusline-MLLKBHHR.js → statusline-VFIA7OS2.js} +3 -3
  29. package/dist/{uninstall-6ZJRS2H4.js → uninstall-QKEC3O64.js} +6 -6
  30. package/dist/{update-G5HZINBK.js → update-IV4Y3T72.js} +6 -6
  31. package/package.json +1 -1
  32. package/dist/chunk-AYIREX73.js +0 -85
  33. package/dist/chunk-TVAY4OQL.js +0 -118
  34. package/dist/config-MCEQOLE5.js +0 -22
  35. package/dist/{chunk-Z5Q533UG.js → chunk-HPNRQYLM.js} +3 -3
  36. package/dist/{update-check-5SCD2JR2.js → update-check-BPCQ25AR.js} +3 -3
@@ -1,6 +1,17 @@
1
+ import {
2
+ listMdFilesRecursive
3
+ } from "./chunk-2DTXVMPQ.js";
4
+ import {
5
+ registerLearnMcp,
6
+ unregisterLearnMcp
7
+ } from "./chunk-WSUYLIYX.js";
1
8
  import {
2
9
  memoryConfigMenu
3
- } from "./chunk-3UIH5U3Y.js";
10
+ } from "./chunk-C5YWESSF.js";
11
+ import {
12
+ resolveLearnDir
13
+ } from "./chunk-B2NFXRFP.js";
14
+ import "./chunk-RZRT7NGT.js";
4
15
  import {
5
16
  findStatuslineHookPath,
6
17
  getCurrentAccountEmail,
@@ -11,16 +22,16 @@ import {
11
22
  saveStatuslineConfig,
12
23
  selectStatuslineComponents,
13
24
  writeStatuslineSettings
14
- } from "./chunk-XOHLBLN2.js";
25
+ } from "./chunk-WNTMKIKC.js";
15
26
  import {
16
27
  ALL_COMPONENT_IDS,
17
28
  DEFAULT_CONFIG
18
- } from "./chunk-OYPOADCX.js";
29
+ } from "./chunk-57EGAXYI.js";
19
30
  import {
20
31
  ensureShellCompletion,
21
32
  hasShellCompletion,
22
33
  removeShellCompletion
23
- } from "./chunk-DMJCO6LJ.js";
34
+ } from "./chunk-RLUNNJWJ.js";
24
35
  import {
25
36
  BACK,
26
37
  applyDocsDirChange,
@@ -30,7 +41,8 @@ import {
30
41
  getScopeLabel,
31
42
  injectBackChoice,
32
43
  showConfigHint
33
- } from "./chunk-Z5Q533UG.js";
44
+ } from "./chunk-HPNRQYLM.js";
45
+ import "./chunk-EVGXUDX4.js";
34
46
  import {
35
47
  claudeLocalSettingsPath,
36
48
  claudeProjectSettingsPath,
@@ -39,9 +51,6 @@ import {
39
51
  localConfigPath,
40
52
  resolvePath
41
53
  } from "./chunk-TWKNGPBO.js";
42
- import {
43
- run
44
- } from "./chunk-EVGXUDX4.js";
45
54
  import {
46
55
  log,
47
56
  printBanner
@@ -58,29 +67,14 @@ import {
58
67
  // src/commands/config.ts
59
68
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
60
69
  import chalk from "chalk";
61
- import { existsSync, readFileSync, writeFileSync } from "fs";
62
- function getNestedFieldScope(section, field, globalCfg, localCfg) {
63
- const globalSection = globalCfg?.[section];
64
- const localSection = localCfg?.[section];
65
- const inGlobal = globalSection?.[field] !== void 0;
66
- const inLocal = localSection?.[field] !== void 0;
67
- if (inGlobal && inLocal) return "both";
68
- if (inGlobal) return "global";
69
- if (inLocal) return "local";
70
- return "-";
71
- }
72
- function getLearnFieldScope(field, globalCfg, localCfg) {
73
- return getNestedFieldScope("learn", field, globalCfg, localCfg);
74
- }
75
- function getMergedNestedValue(section, field, globalCfg, localCfg) {
76
- const localSection = localCfg?.[section];
77
- if (localSection?.[field] !== void 0) return localSection[field];
78
- const globalSection = globalCfg?.[section];
79
- return globalSection?.[field];
80
- }
81
- function getMergedLearnValue(field, globalCfg, localCfg) {
82
- return getMergedNestedValue("learn", field, globalCfg, localCfg);
83
- }
70
+ import {
71
+ cpSync,
72
+ existsSync,
73
+ mkdirSync,
74
+ readFileSync,
75
+ rmSync,
76
+ writeFileSync
77
+ } from "fs";
84
78
  function writeToScope(scope, data) {
85
79
  const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
86
80
  mergeJson(targetPath, data);
@@ -143,43 +137,92 @@ async function editLanguage(globalCfg, localCfg) {
143
137
  if (scope === "back") return;
144
138
  writeToScope(scope, { language: lang });
145
139
  }
146
- async function editLearnOutputDir(globalCfg, localCfg) {
147
- const currentValue = getMergedLearnValue("outputDir", globalCfg, localCfg);
148
- if (currentValue) {
149
- log.dim(`Current: ${currentValue}`);
150
- }
140
+ async function editLearnOutputDir(globalCfg) {
141
+ const defaultLearnDir = DEFAULT_CONFIG.learn.outputDir;
142
+ const oldOutputDir = globalCfg?.learn?.outputDir ?? defaultLearnDir;
143
+ log.dim(`Current: ${oldOutputDir}`);
151
144
  const locationChoice = await select({
152
145
  message: "Where to store learning docs?",
153
146
  choices: [
154
- { name: "In this project (docs/learn/)", value: "local" },
155
- { name: "A separate folder", value: "external" }
147
+ {
148
+ name: `Use default (${defaultLearnDir})`,
149
+ value: "default"
150
+ },
151
+ { name: "Custom path\u2026", value: "custom" }
156
152
  ]
157
153
  });
158
- let outputDir = "docs/learn";
159
- if (locationChoice === "external") {
160
- outputDir = await input({
154
+ let newOutputDir = defaultLearnDir;
155
+ if (locationChoice === "custom") {
156
+ newOutputDir = await input({
161
157
  message: "Enter path (absolute or ~/...):",
162
- default: currentValue ?? void 0,
163
- validate: (val) => val.length > 0 ? true : "Path cannot be empty"
158
+ default: oldOutputDir !== defaultLearnDir ? oldOutputDir : void 0,
159
+ validate: (val) => {
160
+ if (val.length === 0) return "Path cannot be empty";
161
+ if (!val.startsWith("/") && !val.startsWith("~/"))
162
+ return "Path must be absolute (e.g. /home/user/learn) or start with ~/ (e.g. ~/learn)";
163
+ return true;
164
+ }
164
165
  });
165
- const resolved = resolvePath(outputDir);
166
+ const resolved = resolvePath(newOutputDir);
166
167
  if (!existsSync(resolved)) {
167
168
  const create = await confirm({
168
169
  message: `Folder ${resolved} doesn't exist. Create it?`,
169
170
  default: true
170
171
  });
171
172
  if (create) {
172
- run("mkdir", ["-p", resolved]);
173
+ mkdirSync(resolved, { recursive: true });
173
174
  log.success(`Created ${resolved}`);
174
175
  }
175
176
  }
176
177
  }
177
- const scope = await askScope();
178
- if (scope === "back") return;
179
- writeLearnField(scope, "outputDir", outputDir);
178
+ const oldResolved = resolvePath(oldOutputDir);
179
+ const newResolved = resolvePath(newOutputDir);
180
+ writeLearnField("global", "outputDir", newOutputDir);
181
+ if (oldResolved !== newResolved) {
182
+ const mdFiles = existsSync(oldResolved) ? listMdFilesRecursive(oldResolved) : [];
183
+ if (mdFiles.length > 0) {
184
+ const doMove = await confirm({
185
+ message: `Found ${mdFiles.length} learning doc(s) in ${oldResolved}. Move entire folder to ${newResolved}? (overwrites any existing files at target)`,
186
+ default: true
187
+ });
188
+ if (doMove) {
189
+ const existingAtTarget = existsSync(newResolved) ? listMdFilesRecursive(newResolved) : [];
190
+ if (existingAtTarget.length > 0) {
191
+ const doOverwrite = await confirm({
192
+ message: `${newResolved} already contains ${existingAtTarget.length} file(s). Overwrite them?`,
193
+ default: false
194
+ });
195
+ if (!doOverwrite) {
196
+ log.dim("Move cancelled. Config saved with new path.");
197
+ return;
198
+ }
199
+ }
200
+ mkdirSync(newResolved, { recursive: true });
201
+ cpSync(oldResolved, newResolved, { recursive: true });
202
+ rmSync(oldResolved, { recursive: true, force: true });
203
+ log.success(
204
+ `Moved ${mdFiles.length} doc(s) from ${oldResolved} to ${newResolved}.`
205
+ );
206
+ }
207
+ }
208
+ if (!globalCfg?.learn?.disabled) {
209
+ unregisterLearnMcp();
210
+ const registered = registerLearnMcp(newResolved);
211
+ if (registered) {
212
+ log.success(
213
+ `Updated CF Learn MCP to point to ${newResolved}. Restart Claude Code to apply.`
214
+ );
215
+ } else {
216
+ log.warn(
217
+ `Could not re-register MCP. Run manually:
218
+ claude mcp add --scope user coding-friend-learn -- npx -y coding-friend-cli mcp-serve-learn ${newResolved}`
219
+ );
220
+ }
221
+ }
222
+ }
180
223
  }
181
- async function editLearnLanguage(globalCfg, localCfg) {
182
- const currentValue = getMergedLearnValue("language", globalCfg, localCfg);
224
+ async function editLearnLanguage(globalCfg) {
225
+ const currentValue = globalCfg?.learn?.language;
183
226
  if (currentValue) {
184
227
  log.dim(`Current: ${currentValue}`);
185
228
  }
@@ -200,16 +243,10 @@ async function editLearnLanguage(globalCfg, localCfg) {
200
243
  lang = await input({ message: "Enter language name:" });
201
244
  if (!lang) lang = "en";
202
245
  }
203
- const scope = await askScope();
204
- if (scope === "back") return;
205
- writeLearnField(scope, "language", lang);
246
+ writeLearnField("global", "language", lang);
206
247
  }
207
- async function editLearnCategories(globalCfg, localCfg) {
208
- const existingCats = getMergedLearnValue(
209
- "categories",
210
- globalCfg,
211
- localCfg
212
- );
248
+ async function editLearnCategories(globalCfg) {
249
+ const existingCats = globalCfg?.learn?.categories;
213
250
  const defaultNames = DEFAULT_CONFIG.learn.categories.map((c) => c.name).join(", ");
214
251
  const catChoices = [
215
252
  { name: `Use defaults (${defaultNames})`, value: "defaults" }
@@ -260,33 +297,24 @@ async function editLearnCategories(globalCfg, localCfg) {
260
297
  }
261
298
  if (customCats.length > 0) categories = customCats;
262
299
  }
263
- const scope = await askScope();
264
- if (scope === "back") return;
265
- writeLearnField(scope, "categories", categories);
300
+ writeLearnField("global", "categories", categories);
266
301
  }
267
- async function editLearnAutoCommit(globalCfg, localCfg) {
268
- const currentValue = getMergedLearnValue(
269
- "autoCommit",
270
- globalCfg,
271
- localCfg
272
- );
302
+ async function editLearnAutoCommit(globalCfg) {
303
+ const currentValue = globalCfg?.learn?.autoCommit;
273
304
  if (currentValue !== void 0) {
274
305
  log.dim(`Current: ${currentValue}`);
275
306
  }
307
+ log.dim(
308
+ "Note: auto-commit only works if your learn folder is a git repository."
309
+ );
276
310
  const value = await confirm({
277
311
  message: "Auto-commit learning docs to git after each /cf-learn?",
278
312
  default: currentValue ?? false
279
313
  });
280
- const scope = await askScope();
281
- if (scope === "back") return;
282
- writeLearnField(scope, "autoCommit", value);
314
+ writeLearnField("global", "autoCommit", value);
283
315
  }
284
- async function editLearnReadmeIndex(globalCfg, localCfg) {
285
- const currentValue = getMergedLearnValue(
286
- "readmeIndex",
287
- globalCfg,
288
- localCfg
289
- );
316
+ async function editLearnReadmeIndex(globalCfg) {
317
+ const currentValue = globalCfg?.learn?.readmeIndex;
290
318
  if (currentValue !== void 0) {
291
319
  log.dim(`Current: ${currentValue}`);
292
320
  }
@@ -301,62 +329,72 @@ async function editLearnReadmeIndex(globalCfg, localCfg) {
301
329
  let readmeIndex = false;
302
330
  if (indexChoice === "single") readmeIndex = true;
303
331
  else if (indexChoice === "per-category") readmeIndex = "per-category";
304
- const scope = await askScope();
305
- if (scope === "back") return;
306
- writeLearnField(scope, "readmeIndex", readmeIndex);
332
+ writeLearnField("global", "readmeIndex", readmeIndex);
333
+ }
334
+ async function editLearnDisabled(globalCfg) {
335
+ const current = globalCfg?.learn?.disabled ?? false;
336
+ log.dim(`Current: ${current ? "disabled" : "enabled"}`);
337
+ const value = await confirm({
338
+ message: current ? "CF Learn is disabled. Re-enable it? (resumes file writing and re-registers MCP)" : "Disable CF Learn? (stops file writing and MCP serving)",
339
+ default: !current
340
+ });
341
+ writeLearnField("global", "disabled", value ? !current : current);
342
+ if (!current && value) {
343
+ const unregistered = unregisterLearnMcp();
344
+ if (unregistered) {
345
+ log.dim(
346
+ "Removed coding-friend-learn from Claude Code MCP. Restart Claude Code to apply."
347
+ );
348
+ } else {
349
+ log.warn(
350
+ "Could not unregister MCP automatically. Run manually:\n claude mcp remove --scope user coding-friend-learn"
351
+ );
352
+ }
353
+ } else if (current && value) {
354
+ const freshGlobal = readJson(globalConfigPath());
355
+ const learnDir = resolveLearnDir(freshGlobal);
356
+ registerLearnMcp(learnDir);
357
+ log.dim(
358
+ "Re-registered coding-friend-learn. Restart Claude Code to activate."
359
+ );
360
+ }
307
361
  }
308
362
  async function learnSubMenu() {
309
363
  while (true) {
310
364
  const globalCfg = readJson(globalConfigPath());
311
- const localCfg = readJson(localConfigPath());
312
- const outputDirScope = getLearnFieldScope("outputDir", globalCfg, localCfg);
313
- const outputDirVal = getMergedLearnValue(
314
- "outputDir",
315
- globalCfg,
316
- localCfg
317
- );
318
- const langScope = getLearnFieldScope("language", globalCfg, localCfg);
319
- const langVal = getMergedLearnValue("language", globalCfg, localCfg);
320
- const catScope = getLearnFieldScope("categories", globalCfg, localCfg);
321
- const autoCommitScope = getLearnFieldScope(
322
- "autoCommit",
323
- globalCfg,
324
- localCfg
325
- );
326
- const autoCommitVal = getMergedLearnValue(
327
- "autoCommit",
328
- globalCfg,
329
- localCfg
330
- );
331
- const readmeScope = getLearnFieldScope("readmeIndex", globalCfg, localCfg);
332
- const readmeVal = getMergedLearnValue(
333
- "readmeIndex",
334
- globalCfg,
335
- localCfg
336
- );
365
+ const outputDirVal = globalCfg?.learn?.outputDir ?? DEFAULT_CONFIG.learn.outputDir;
366
+ const langVal = globalCfg?.learn?.language;
367
+ const autoCommitVal = globalCfg?.learn?.autoCommit;
368
+ const readmeVal = globalCfg?.learn?.readmeIndex;
369
+ const isDisabled = globalCfg?.learn?.disabled ?? false;
370
+ const disabledLabel = isDisabled ? chalk.yellow(" [disabled]") : "";
337
371
  const choice = await select({
338
- message: "Learn settings:",
372
+ message: `Learn settings${disabledLabel}:`,
339
373
  choices: injectBackChoice(
340
374
  [
341
375
  {
342
- name: `Output dir ${formatScopeLabel(outputDirScope)}${outputDirVal ? ` (${outputDirVal})` : ""}`,
376
+ name: `Output dir (${outputDirVal})`,
343
377
  value: "outputDir"
344
378
  },
345
379
  {
346
- name: `Language ${formatScopeLabel(langScope)}${langVal ? ` (${langVal})` : ""}`,
380
+ name: `Language${langVal ? ` (${langVal})` : ""}`,
347
381
  value: "language"
348
382
  },
349
383
  {
350
- name: `Categories ${formatScopeLabel(catScope)}`,
384
+ name: "Categories",
351
385
  value: "categories"
352
386
  },
353
387
  {
354
- name: `Auto-commit ${formatScopeLabel(autoCommitScope)}${autoCommitVal !== void 0 ? ` (${autoCommitVal})` : ""}`,
388
+ name: `Auto-commit${autoCommitVal !== void 0 ? ` (${autoCommitVal})` : ""}`,
355
389
  value: "autoCommit"
356
390
  },
357
391
  {
358
- name: `README index ${formatScopeLabel(readmeScope)}${readmeVal !== void 0 ? ` (${readmeVal})` : ""}`,
392
+ name: `README index${readmeVal !== void 0 ? ` (${readmeVal})` : ""}`,
359
393
  value: "readmeIndex"
394
+ },
395
+ {
396
+ name: isDisabled ? "Enable CF Learn" : "Disable CF Learn",
397
+ value: "disabled"
360
398
  }
361
399
  ],
362
400
  "Back"
@@ -365,23 +403,39 @@ async function learnSubMenu() {
365
403
  if (choice === BACK) return;
366
404
  switch (choice) {
367
405
  case "outputDir":
368
- await editLearnOutputDir(globalCfg, localCfg);
406
+ await editLearnOutputDir(globalCfg);
369
407
  break;
370
408
  case "language":
371
- await editLearnLanguage(globalCfg, localCfg);
409
+ await editLearnLanguage(globalCfg);
372
410
  break;
373
411
  case "categories":
374
- await editLearnCategories(globalCfg, localCfg);
412
+ await editLearnCategories(globalCfg);
375
413
  break;
376
414
  case "autoCommit":
377
- await editLearnAutoCommit(globalCfg, localCfg);
415
+ await editLearnAutoCommit(globalCfg);
378
416
  break;
379
417
  case "readmeIndex":
380
- await editLearnReadmeIndex(globalCfg, localCfg);
418
+ await editLearnReadmeIndex(globalCfg);
419
+ break;
420
+ case "disabled":
421
+ await editLearnDisabled(globalCfg);
381
422
  break;
382
423
  }
383
424
  }
384
425
  }
426
+ async function editTdd(globalCfg, localCfg) {
427
+ const currentValue = getMergedValue("tdd", globalCfg, localCfg);
428
+ if (currentValue !== void 0) {
429
+ log.dim(`Current: ${currentValue}`);
430
+ }
431
+ const value = await confirm({
432
+ message: "Enable TDD by default? (writes failing tests before code \u2014 RED \u2192 GREEN \u2192 REFACTOR)",
433
+ default: currentValue ?? false
434
+ });
435
+ const scope = await askScope();
436
+ if (scope === "back") return;
437
+ writeToScope(scope, { tdd: value });
438
+ }
385
439
  async function editAutoApprove(globalCfg, localCfg) {
386
440
  const currentValue = getMergedValue("autoApprove", globalCfg, localCfg);
387
441
  if (currentValue !== void 0) {
@@ -411,68 +465,6 @@ async function editAutoApprove(globalCfg, localCfg) {
411
465
  log.dim("Docs: https://cf.dinhanhthi.com/docs/reference/auto-approve/");
412
466
  }
413
467
  }
414
- function buildCodexConfig(input2) {
415
- return {
416
- enabled: input2.enabled,
417
- modes: input2.modes,
418
- effort: input2.effort
419
- };
420
- }
421
- async function editCodex(globalCfg, localCfg) {
422
- const current = localCfg?.codex ?? globalCfg?.codex;
423
- const defaults = DEFAULT_CONFIG.codex;
424
- if (current !== void 0) {
425
- log.dim(
426
- `Current: enabled=${current.enabled}, modes=${(current.modes ?? []).join(",") || "(none)"}, effort=${current.effort}`
427
- );
428
- }
429
- const enabled = await confirm({
430
- message: "Enable Codex as 6th specialist in /cf-review? (requires `codex` CLI in PATH)",
431
- default: current?.enabled ?? defaults.enabled
432
- });
433
- const modes = await checkbox({
434
- message: "Which review modes should Codex run in?",
435
- choices: [
436
- { name: "QUICK (\u22643 files, \u226450 lines)", value: "QUICK" },
437
- { name: "STANDARD (4\u201310 files or 51\u2013300 lines)", value: "STANDARD" },
438
- {
439
- name: "DEEP (>10 files, >300 lines, or sensitive paths)",
440
- value: "DEEP"
441
- }
442
- ],
443
- default: current?.modes ?? defaults.modes
444
- });
445
- if (modes.length === 0) {
446
- log.dim(
447
- "Warning: no modes selected \u2014 Codex will never run. Select at least one mode."
448
- );
449
- }
450
- const effort = await select({
451
- message: "Codex reasoning effort level:",
452
- choices: [
453
- { name: "minimal \u2014 fastest, least thorough", value: "minimal" },
454
- { name: "low", value: "low" },
455
- { name: "medium (default)", value: "medium" },
456
- { name: "high", value: "high" },
457
- { name: "xhigh \u2014 slowest, most thorough", value: "xhigh" }
458
- ],
459
- default: current?.effort ?? defaults.effort
460
- });
461
- const scope = await askScope();
462
- if (scope === "back") return;
463
- const codexCfg = buildCodexConfig({ enabled, modes, effort });
464
- writeToScope(scope, { codex: codexCfg });
465
- if (enabled) {
466
- log.dim(
467
- "Tip: make sure `codex` CLI is installed \u2014 `npm install -g @openai/codex`"
468
- );
469
- log.dim(
470
- "Docs: https://cf.dinhanhthi.com/docs/configuration/config-json/#codex"
471
- );
472
- } else {
473
- log.dim("Codex disabled.");
474
- }
475
- }
476
468
  async function editStatusline() {
477
469
  const hookResult = findStatuslineHookPath();
478
470
  if (!hookResult) {
@@ -525,7 +517,6 @@ async function editGitignore(globalCfg, localCfg) {
525
517
  `${docsDir}/plans/`,
526
518
  `${docsDir}/memory/`,
527
519
  `${docsDir}/research/`,
528
- `${docsDir}/learn/`,
529
520
  `${docsDir}/sessions/`,
530
521
  `${docsDir}/reviews/`,
531
522
  `${docsDir}/context/`,
@@ -653,7 +644,7 @@ async function editPermissions() {
653
644
  )
654
645
  });
655
646
  if (choice === BACK) return;
656
- const { permissionCommand } = await import("./permission-DNW7DE24.js");
647
+ const { permissionCommand } = await import("./permission-C5R3LMZQ.js");
657
648
  switch (choice) {
658
649
  case "interactive":
659
650
  await permissionCommand({});
@@ -680,14 +671,14 @@ async function configCommand() {
680
671
  const langVal = getMergedValue("language", globalCfg, localCfg);
681
672
  const learnScope = getScopeLabel("learn", globalCfg, localCfg);
682
673
  const memoryScope = getScopeLabel("memory", globalCfg, localCfg);
674
+ const tddScope = getScopeLabel("tdd", globalCfg, localCfg);
675
+ const tddVal = getMergedValue("tdd", globalCfg, localCfg);
683
676
  const autoApproveScope = getScopeLabel("autoApprove", globalCfg, localCfg);
684
677
  const autoApproveVal = getMergedValue(
685
678
  "autoApprove",
686
679
  globalCfg,
687
680
  localCfg
688
681
  );
689
- const codexScope = getScopeLabel("codex", globalCfg, localCfg);
690
- const codexEnabled = getMergedValue("codex", globalCfg, localCfg)?.enabled;
691
682
  const statuslineStatus = isStatuslineConfigured() ? chalk.green("configured") : chalk.yellow("not configured");
692
683
  const completionStatus = hasShellCompletion() ? chalk.green("installed") : chalk.yellow("not installed");
693
684
  const projectRules = getExistingRules(claudeLocalSettingsPath());
@@ -723,16 +714,16 @@ async function configCommand() {
723
714
  value: "memory",
724
715
  description: " Tier, auto-capture, auto-start, embedding provider"
725
716
  },
717
+ {
718
+ name: `TDD ${formatScopeLabel(tddScope)}${tddVal !== void 0 ? ` (${tddVal})` : ""}`,
719
+ value: "tdd",
720
+ description: " Enable TDD (RED\u2192GREEN\u2192REFACTOR) by default for all implementations"
721
+ },
726
722
  {
727
723
  name: `Auto-approve ${formatScopeLabel(autoApproveScope)}${autoApproveVal !== void 0 ? ` (${autoApproveVal})` : ""}`,
728
724
  value: "autoApprove",
729
725
  description: " Auto-approve safe tool calls, block destructive ones, prompt for ambiguous"
730
726
  },
731
- {
732
- name: `Codex cross-engine review ${formatScopeLabel(codexScope)}${codexEnabled !== void 0 ? ` (${codexEnabled ? "enabled" : "disabled"})` : ""}`,
733
- value: "codex",
734
- description: " Enable OpenAI Codex as 6th specialist in /cf-review (opt-in)"
735
- },
736
727
  {
737
728
  name: `Statusline (${statuslineStatus})`,
738
729
  value: "statusline",
@@ -773,12 +764,12 @@ async function configCommand() {
773
764
  case "memory":
774
765
  await memoryConfigMenu();
775
766
  break;
767
+ case "tdd":
768
+ await editTdd(globalCfg, localCfg);
769
+ break;
776
770
  case "autoApprove":
777
771
  await editAutoApprove(globalCfg, localCfg);
778
772
  break;
779
- case "codex":
780
- await editCodex(globalCfg, localCfg);
781
- break;
782
773
  case "statusline":
783
774
  await editStatusline();
784
775
  break;
@@ -795,9 +786,6 @@ async function configCommand() {
795
786
  console.log();
796
787
  }
797
788
  }
798
-
799
789
  export {
800
- buildCodexConfig,
801
- editCodex,
802
790
  configCommand
803
791
  };
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  ensureStatusline
3
- } from "./chunk-XOHLBLN2.js";
4
- import "./chunk-OYPOADCX.js";
3
+ } from "./chunk-WNTMKIKC.js";
4
+ import "./chunk-57EGAXYI.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-DMJCO6LJ.js";
7
+ } from "./chunk-RLUNNJWJ.js";
8
8
  import {
9
9
  isMarketplaceRegistered,
10
10
  isPluginInstalled
11
11
  } from "./chunk-R6HDMRYL.js";
12
+ import {
13
+ commandExists,
14
+ run
15
+ } from "./chunk-EVGXUDX4.js";
12
16
  import {
13
17
  devStatePath,
14
18
  knownMarketplacesPath,
15
19
  pluginCachePath
16
20
  } from "./chunk-TWKNGPBO.js";
17
- import {
18
- commandExists,
19
- run
20
- } from "./chunk-EVGXUDX4.js";
21
21
  import {
22
22
  log,
23
23
  printBanner
@@ -4,9 +4,9 @@ import {
4
4
  } from "./chunk-R6HDMRYL.js";
5
5
  import {
6
6
  resolveScope
7
- } from "./chunk-Z5Q533UG.js";
8
- import "./chunk-TWKNGPBO.js";
7
+ } from "./chunk-HPNRQYLM.js";
9
8
  import "./chunk-EVGXUDX4.js";
9
+ import "./chunk-TWKNGPBO.js";
10
10
  import {
11
11
  log
12
12
  } from "./chunk-NREZK463.js";
@@ -4,9 +4,9 @@ import {
4
4
  } from "./chunk-R6HDMRYL.js";
5
5
  import {
6
6
  resolveScope
7
- } from "./chunk-Z5Q533UG.js";
8
- import "./chunk-TWKNGPBO.js";
7
+ } from "./chunk-HPNRQYLM.js";
9
8
  import "./chunk-EVGXUDX4.js";
9
+ import "./chunk-TWKNGPBO.js";
10
10
  import {
11
11
  log
12
12
  } from "./chunk-NREZK463.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  resolveDocsDir
3
- } from "./chunk-K6EDQGDN.js";
3
+ } from "./chunk-B2NFXRFP.js";
4
4
  import {
5
5
  getLibPath
6
6
  } from "./chunk-RZRT7NGT.js";
7
- import "./chunk-OYPOADCX.js";
8
- import "./chunk-TWKNGPBO.js";
7
+ import "./chunk-57EGAXYI.js";
9
8
  import {
10
9
  run,
11
10
  streamExec
12
11
  } from "./chunk-EVGXUDX4.js";
12
+ import "./chunk-TWKNGPBO.js";
13
13
  import {
14
14
  log,
15
15
  printBanner