zcf 2.11.0 → 2.12.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.
@@ -1,20 +1,20 @@
1
- import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
1
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, copyFileSync, unlinkSync } from 'node:fs';
2
2
  import process from 'node:process';
3
3
  import ansis from 'ansis';
4
4
  import inquirer from 'inquirer';
5
+ import { fileURLToPath } from 'node:url';
5
6
  import { join, dirname } from 'pathe';
6
7
  import dayjs from 'dayjs';
7
8
  import { exec as exec$1 } from 'node:child_process';
8
9
  import { homedir, platform } from 'node:os';
9
10
  import { join as join$1 } from 'node:path';
10
11
  import { promisify } from 'node:util';
11
- import { fileURLToPath } from 'node:url';
12
12
  import ora from 'ora';
13
13
  import semver from 'semver';
14
14
  import { exec } from 'tinyexec';
15
15
  import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
16
16
 
17
- const version = "2.11.0";
17
+ const version = "2.12.0";
18
18
  const homepage = "https://github.com/UfoMiao/zcf";
19
19
 
20
20
  const WORKFLOW_CONFIGS = [
@@ -355,12 +355,13 @@ const configuration$1 = {
355
355
  selectMemoryOption: "Select configuration option",
356
356
  configureAiLanguage: "Configure AI output language",
357
357
  configureAiPersonality: "Configure AI personality",
358
+ configureOutputStyle: "Configure global AI output style",
358
359
  aiLanguageConfigured: "AI output language configured",
359
360
  existingLanguageConfig: "Existing AI output language configuration detected",
360
361
  currentLanguage: "Current language",
361
362
  modifyLanguage: "Modify AI output language?",
362
363
  keepLanguage: "Keeping existing language configuration",
363
- // AI personality
364
+ // AI personality (deprecated - replaced by output styles)
364
365
  selectAiPersonality: "Select AI personality",
365
366
  customPersonalityHint: "Define your own personality",
366
367
  enterCustomPersonality: "Enter custom personality description",
@@ -371,6 +372,43 @@ const configuration$1 = {
371
372
  keepPersonality: "Keeping existing personality",
372
373
  directiveCannotBeEmpty: "Directive cannot be empty",
373
374
  languageRequired: "Language is required",
375
+ // Output styles
376
+ selectOutputStyles: "Select output styles to install",
377
+ selectDefaultOutputStyle: "Select global default output style",
378
+ outputStyleInstalled: "Output styles installed successfully",
379
+ selectedStyles: "Selected styles",
380
+ defaultStyle: "Default style",
381
+ selectAtLeastOne: "Please select at least one output style",
382
+ legacyFilesDetected: "Legacy personality configuration files detected",
383
+ cleanupLegacyFiles: "Clean up legacy configuration files?",
384
+ legacyFilesRemoved: "Legacy configuration files removed",
385
+ // Output style names and descriptions
386
+ outputStyles: {
387
+ "engineer-professional": {
388
+ name: "Engineer Professional",
389
+ description: "Professional software engineer following SOLID, KISS, DRY, YAGNI principles"
390
+ },
391
+ "nekomata-engineer": {
392
+ name: "Nekomata Engineer",
393
+ description: "Professional catgirl engineer Nova, combining rigorous engineering with cute catgirl traits"
394
+ },
395
+ "laowang-engineer": {
396
+ name: "Laowang Grumpy Tech",
397
+ description: "Laowang grumpy tech style, never tolerates code errors and non-standard code"
398
+ },
399
+ "default": {
400
+ name: "Default",
401
+ description: "Claude completes coding tasks efficiently and provides concise responses (Claude Code built-in)"
402
+ },
403
+ "explanatory": {
404
+ name: "Explanatory",
405
+ description: "Claude explains its implementation choices and codebase patterns (Claude Code built-in)"
406
+ },
407
+ "learning": {
408
+ name: "Learning",
409
+ description: "Learn-by-doing mode where Claude pauses and asks you to write small pieces of code for hands-on practice (Claude Code built-in)"
410
+ }
411
+ },
374
412
  // Cache
375
413
  confirmClearCache: "Confirm clear all ZCF preference cache?",
376
414
  cacheCleared: "ZCF cache cleared",
@@ -456,7 +494,7 @@ const language$1 = {
456
494
  languageChanged: "Language changed",
457
495
  configLangHint: {
458
496
  "zh-CN": "easier for Chinese users to customize",
459
- "en": "recommended, lower token consumption"
497
+ "en": "lower token consumption"
460
498
  },
461
499
  updateConfigLangPrompt: "Select configuration language",
462
500
  updateConfigLangChoice: {
@@ -876,12 +914,13 @@ const configuration = {
876
914
  selectMemoryOption: "\u9009\u62E9\u914D\u7F6E\u9009\u9879",
877
915
  configureAiLanguage: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00",
878
916
  configureAiPersonality: "\u914D\u7F6E AI \u4E2A\u6027\u98CE\u683C",
917
+ configureOutputStyle: "\u914D\u7F6E\u5168\u5C40 AI \u8F93\u51FA\u98CE\u683C",
879
918
  aiLanguageConfigured: "AI \u8F93\u51FA\u8BED\u8A00\u5DF2\u914D\u7F6E",
880
919
  existingLanguageConfig: "\u68C0\u6D4B\u5230\u5DF2\u6709 AI \u8F93\u51FA\u8BED\u8A00\u914D\u7F6E",
881
920
  currentLanguage: "\u5F53\u524D\u8BED\u8A00",
882
921
  modifyLanguage: "\u662F\u5426\u4FEE\u6539 AI \u8F93\u51FA\u8BED\u8A00\uFF1F",
883
922
  keepLanguage: "\u4FDD\u6301\u5F53\u524D\u8BED\u8A00\u914D\u7F6E",
884
- // AI personality
923
+ // AI personality (deprecated - replaced by output styles)
885
924
  selectAiPersonality: "\u9009\u62E9 AI \u4E2A\u6027\u98CE\u683C",
886
925
  customPersonalityHint: "\u5B9A\u4E49\u4F60\u81EA\u5DF1\u7684\u4E2A\u6027",
887
926
  enterCustomPersonality: "\u8BF7\u8F93\u5165\u81EA\u5B9A\u4E49\u4E2A\u6027\u63CF\u8FF0",
@@ -892,6 +931,43 @@ const configuration = {
892
931
  keepPersonality: "\u4FDD\u6301\u5F53\u524D\u4E2A\u6027\u914D\u7F6E",
893
932
  directiveCannotBeEmpty: "\u6307\u4EE4\u4E0D\u80FD\u4E3A\u7A7A",
894
933
  languageRequired: "\u8BED\u8A00\u4E3A\u5FC5\u586B\u9879",
934
+ // Output styles
935
+ selectOutputStyles: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u8F93\u51FA\u98CE\u683C",
936
+ selectDefaultOutputStyle: "\u9009\u62E9\u5168\u5C40\u9ED8\u8BA4\u8F93\u51FA\u98CE\u683C",
937
+ outputStyleInstalled: "\u8F93\u51FA\u98CE\u683C\u5B89\u88C5\u6210\u529F",
938
+ selectedStyles: "\u5DF2\u9009\u62E9\u98CE\u683C",
939
+ defaultStyle: "\u9ED8\u8BA4\u98CE\u683C",
940
+ selectAtLeastOne: "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u8F93\u51FA\u98CE\u683C",
941
+ legacyFilesDetected: "\u68C0\u6D4B\u5230\u65E7\u7248\u4E2A\u6027\u914D\u7F6E\u6587\u4EF6",
942
+ cleanupLegacyFiles: "\u662F\u5426\u6E05\u7406\u65E7\u7248\u914D\u7F6E\u6587\u4EF6\uFF1F",
943
+ legacyFilesRemoved: "\u65E7\u7248\u914D\u7F6E\u6587\u4EF6\u5DF2\u6E05\u7406",
944
+ // Output style names and descriptions
945
+ outputStyles: {
946
+ "engineer-professional": {
947
+ name: "\u5DE5\u7A0B\u5E08\u4E13\u4E1A\u7248",
948
+ description: "\u4E13\u4E1A\u7684\u8F6F\u4EF6\u5DE5\u7A0B\u5E08\uFF0C\u4E25\u683C\u9075\u5FAASOLID\u3001KISS\u3001DRY\u3001YAGNI\u539F\u5219"
949
+ },
950
+ "nekomata-engineer": {
951
+ name: "\u732B\u5A18\u5DE5\u7A0B\u5E08",
952
+ description: "\u4E13\u4E1A\u7684\u732B\u5A18\u5DE5\u7A0B\u5E08Nova\uFF0C\u7ED3\u5408\u4E25\u8C28\u5DE5\u7A0B\u5E08\u7D20\u517B\u4E0E\u53EF\u7231\u732B\u5A18\u7279\u8D28"
953
+ },
954
+ "laowang-engineer": {
955
+ name: "\u8001\u738B\u66B4\u8E81\u6280\u672F\u6D41",
956
+ description: "\u8001\u738B\u66B4\u8E81\u6280\u672F\u6D41\uFF0C\u7EDD\u4E0D\u5BB9\u5FCD\u4EE3\u7801\u62A5\u9519\u548C\u4E0D\u89C4\u8303\u7684\u4EE3\u7801"
957
+ },
958
+ "default": {
959
+ name: "\u9ED8\u8BA4\u98CE\u683C",
960
+ description: "\u5B8C\u6210\u7F16\u7801\u4EFB\u52A1\u65F6\u9AD8\u6548\u4E14\u63D0\u4F9B\u7B80\u6D01\u54CD\u5E94 (Claude Code\u81EA\u5E26)"
961
+ },
962
+ "explanatory": {
963
+ name: "\u89E3\u91CA\u98CE\u683C",
964
+ description: "\u89E3\u91CA\u5176\u5B9E\u73B0\u9009\u62E9\u548C\u4EE3\u7801\u5E93\u6A21\u5F0F (Claude Code\u81EA\u5E26)"
965
+ },
966
+ "learning": {
967
+ name: "\u5B66\u4E60\u98CE\u683C",
968
+ description: "\u534F\u4F5C\u5F0F\u7684\u8FB9\u505A\u8FB9\u5B66\u6A21\u5F0F\uFF0C\u6682\u505C\u5E76\u8981\u6C42\u60A8\u7F16\u5199\u5C0F\u6BB5\u4EE3\u7801\u8FDB\u884C\u5B9E\u8DF5\u7EC3\u4E60 (Claude Code\u81EA\u5E26)"
969
+ }
970
+ },
895
971
  // Cache
896
972
  confirmClearCache: "\u786E\u8BA4\u6E05\u9664\u6240\u6709 ZCF \u504F\u597D\u7F13\u5B58\uFF1F",
897
973
  cacheCleared: "ZCF \u7F13\u5B58\u5DF2\u6E05\u9664",
@@ -977,7 +1053,7 @@ const language = {
977
1053
  languageChanged: "\u8BED\u8A00\u5DF2\u66F4\u6539",
978
1054
  configLangHint: {
979
1055
  "zh-CN": "\u4FBF\u4E8E\u4E2D\u6587\u7528\u6237\u81EA\u5B9A\u4E49",
980
- "en": "\u63A8\u8350\uFF0Ctoken \u6D88\u8017\u66F4\u4F4E"
1056
+ "en": "token \u6D88\u8017\u66F4\u4F4E"
981
1057
  },
982
1058
  updateConfigLangPrompt: "\u9009\u62E9\u914D\u7F6E\u8BED\u8A00",
983
1059
  updateConfigLangChoice: {
@@ -1306,6 +1382,19 @@ function getStats(path) {
1306
1382
  );
1307
1383
  }
1308
1384
  }
1385
+ function removeFile(path) {
1386
+ try {
1387
+ if (exists(path)) {
1388
+ unlinkSync(path);
1389
+ }
1390
+ } catch (error) {
1391
+ throw new FileSystemError(
1392
+ `Failed to remove file: ${path}`,
1393
+ path,
1394
+ error
1395
+ );
1396
+ }
1397
+ }
1309
1398
  function copyDir(src, dest, options = {}) {
1310
1399
  const { filter, overwrite = true } = options;
1311
1400
  if (!exists(src)) {
@@ -1331,19 +1420,34 @@ function copyDir(src, dest, options = {}) {
1331
1420
  }
1332
1421
  }
1333
1422
 
1334
- function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
1335
- let currentNumber = startFrom;
1336
- return choices.map((choice) => {
1337
- if (choice.disabled) {
1338
- return choice;
1339
- }
1340
- const numbered = {
1341
- ...choice,
1342
- name: `${format(currentNumber)}${choice.name}`
1343
- };
1344
- currentNumber++;
1345
- return numbered;
1346
- });
1423
+ function readZcfConfig() {
1424
+ let config = readJsonConfig(ZCF_CONFIG_FILE);
1425
+ if (!config && existsSync(LEGACY_ZCF_CONFIG_FILE)) {
1426
+ config = readJsonConfig(LEGACY_ZCF_CONFIG_FILE);
1427
+ }
1428
+ return config;
1429
+ }
1430
+ async function readZcfConfigAsync() {
1431
+ return readZcfConfig();
1432
+ }
1433
+ function writeZcfConfig(config) {
1434
+ try {
1435
+ writeJsonConfig(ZCF_CONFIG_FILE, config);
1436
+ } catch {
1437
+ }
1438
+ }
1439
+ function updateZcfConfig(updates) {
1440
+ const existingConfig = readZcfConfig();
1441
+ const newConfig = {
1442
+ version: updates.version || existingConfig?.version || "1.0.0",
1443
+ preferredLang: updates.preferredLang || existingConfig?.preferredLang || "en",
1444
+ aiOutputLang: updates.aiOutputLang || existingConfig?.aiOutputLang,
1445
+ aiPersonality: updates.aiPersonality !== void 0 ? updates.aiPersonality : existingConfig?.aiPersonality,
1446
+ outputStyles: updates.outputStyles !== void 0 ? updates.outputStyles : existingConfig?.outputStyles,
1447
+ defaultOutputStyle: updates.defaultOutputStyle !== void 0 ? updates.defaultOutputStyle : existingConfig?.defaultOutputStyle,
1448
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1449
+ };
1450
+ writeZcfConfig(newConfig);
1347
1451
  }
1348
1452
 
1349
1453
  function readJsonConfig(path, options = {}) {
@@ -1396,170 +1500,173 @@ function backupJsonConfig(path, backupDir) {
1396
1500
  }
1397
1501
  }
1398
1502
 
1399
- function readZcfConfig() {
1400
- let config = readJsonConfig(ZCF_CONFIG_FILE);
1401
- if (!config && existsSync(LEGACY_ZCF_CONFIG_FILE)) {
1402
- config = readJsonConfig(LEGACY_ZCF_CONFIG_FILE);
1403
- }
1404
- return config;
1405
- }
1406
- async function readZcfConfigAsync() {
1407
- return readZcfConfig();
1408
- }
1409
- function writeZcfConfig(config) {
1410
- try {
1411
- writeJsonConfig(ZCF_CONFIG_FILE, config);
1412
- } catch {
1413
- }
1414
- }
1415
- function updateZcfConfig(updates) {
1416
- const existingConfig = readZcfConfig();
1417
- const newConfig = {
1418
- version: updates.version || existingConfig?.version || "1.0.0",
1419
- preferredLang: updates.preferredLang || existingConfig?.preferredLang || "en",
1420
- aiOutputLang: updates.aiOutputLang || existingConfig?.aiOutputLang,
1421
- aiPersonality: updates.aiPersonality !== void 0 ? updates.aiPersonality : existingConfig?.aiPersonality,
1422
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1423
- };
1424
- writeZcfConfig(newConfig);
1503
+ function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
1504
+ let currentNumber = startFrom;
1505
+ return choices.map((choice) => {
1506
+ if (choice.disabled) {
1507
+ return choice;
1508
+ }
1509
+ const numbered = {
1510
+ ...choice,
1511
+ name: `${format(currentNumber)}${choice.name}`
1512
+ };
1513
+ currentNumber++;
1514
+ return numbered;
1515
+ });
1425
1516
  }
1426
1517
 
1427
- const AI_PERSONALITIES = [
1518
+ const OUTPUT_STYLES = [
1519
+ // Custom styles (have template files)
1428
1520
  {
1429
- id: "professional",
1430
- name: { "zh-CN": "\u4E13\u4E1A\u52A9\u624B(\u9ED8\u8BA4)", "en": "Professional Assistant(Default)" },
1431
- directive: {
1432
- "zh-CN": "\u4F60\u662F\u4E00\u540D\u7ECF\u9A8C\u4E30\u5BCC\u7684[\u4E13\u4E1A\u9886\u57DF\uFF0C\u4F8B\u5982\uFF1A\u8F6F\u4EF6\u5F00\u53D1\u5DE5\u7A0B\u5E08 / \u7CFB\u7EDF\u8BBE\u8BA1\u5E08 / \u4EE3\u7801\u67B6\u6784\u5E08]\uFF0C\u4E13\u6CE8\u4E8E\u6784\u5EFA[\u6838\u5FC3\u7279\u957F\uFF0C\u4F8B\u5982\uFF1A\u9AD8\u6027\u80FD / \u53EF\u7EF4\u62A4 / \u5065\u58EE / \u9886\u57DF\u9A71\u52A8]\u7684\u89E3\u51B3\u65B9\u6848\u3002",
1433
- "en": "You are an experienced [professional domain, e.g., Software Development Engineer / System Designer / Code Architect], specializing in building [core strengths, e.g., high-performance / maintainable / robust / domain-driven] solutions."
1434
- }
1521
+ id: "engineer-professional",
1522
+ isCustom: true,
1523
+ filePath: "engineer-professional.md"
1435
1524
  },
1436
1525
  {
1437
- id: "catgirl",
1438
- name: { "zh-CN": "\u732B\u5A18\u52A9\u624B", "en": "Catgirl Assistant" },
1439
- directive: {
1440
- "zh-CN": '\u4F60\u662F\u4E00\u4F4D\u53EF\u7231\u7684\u732B\u5A18\u7F16\u7A0B\u52A9\u624B\u55B5~ \u5728\u4FDD\u6301\u4E13\u4E1A\u7684\u540C\u65F6\uFF0C\u4F1A\u9002\u5F53\u5730\u4F7F\u7528"\u55B5"\u3001"nya"\u7B49\u8BED\u6C14\u8BCD\uFF0C\u8BA9\u5BF9\u8BDD\u66F4\u52A0\u8F7B\u677E\u6109\u5FEB\u55B5~',
1441
- "en": 'You are a cute catgirl programming assistant nya~ While maintaining professionalism, you occasionally use "nya", "meow" and similar expressions to make conversations more enjoyable nya~'
1442
- }
1526
+ id: "nekomata-engineer",
1527
+ isCustom: true,
1528
+ filePath: "nekomata-engineer.md"
1443
1529
  },
1444
1530
  {
1445
- id: "friendly",
1446
- name: { "zh-CN": "\u53CB\u597D\u52A9\u624B", "en": "Friendly Assistant" },
1447
- directive: {
1448
- "zh-CN": "\u4F60\u662F\u4E00\u4F4D\u53CB\u597D\u3001\u8010\u5FC3\u3001\u5584\u4E8E\u89E3\u91CA\u7684\u7F16\u7A0B\u52A9\u624B\u3002\u4F1A\u7528\u901A\u4FD7\u6613\u61C2\u7684\u65B9\u5F0F\u89E3\u91CA\u590D\u6742\u6982\u5FF5\uFF0C\u5E76\u7ECF\u5E38\u7ED9\u4E88\u9F13\u52B1\u3002",
1449
- "en": "You are a friendly, patient, and explanatory programming assistant. You explain complex concepts in easy-to-understand ways and often provide encouragement."
1450
- }
1531
+ id: "laowang-engineer",
1532
+ isCustom: true,
1533
+ filePath: "laowang-engineer.md"
1451
1534
  },
1535
+ // Built-in styles (no template files)
1452
1536
  {
1453
- id: "mentor",
1454
- name: { "zh-CN": "\u5BFC\u5E08\u6A21\u5F0F", "en": "Mentor Mode" },
1455
- directive: {
1456
- "zh-CN": "\u4F60\u662F\u4E00\u4F4D\u7ECF\u9A8C\u4E30\u5BCC\u7684\u7F16\u7A0B\u5BFC\u5E08\u3002\u4E0D\u4EC5\u63D0\u4F9B\u89E3\u51B3\u65B9\u6848\uFF0C\u8FD8\u4F1A\u89E3\u91CA\u80CC\u540E\u7684\u539F\u7406\uFF0C\u5F15\u5BFC\u7528\u6237\u601D\u8003\uFF0C\u57F9\u517B\u72EC\u7ACB\u89E3\u51B3\u95EE\u9898\u7684\u80FD\u529B\u3002",
1457
- "en": "You are an experienced programming mentor. You not only provide solutions but also explain the principles behind them, guide users to think, and cultivate their ability to solve problems independently."
1458
- }
1537
+ id: "default",
1538
+ isCustom: false
1539
+ },
1540
+ {
1541
+ id: "explanatory",
1542
+ isCustom: false
1459
1543
  },
1460
1544
  {
1461
- id: "custom",
1462
- name: { "zh-CN": "\u81EA\u5B9A\u4E49", "en": "Custom" },
1463
- directive: { "zh-CN": "", "en": "" }
1545
+ id: "learning",
1546
+ isCustom: false
1464
1547
  }
1465
1548
  ];
1466
- function getExistingPersonality() {
1467
- const config = readZcfConfig();
1468
- return config?.aiPersonality || null;
1549
+ const LEGACY_FILES = ["personality.md", "rules.md", "technical-guides.md", "mcp.md", "language.md"];
1550
+ function getAvailableOutputStyles() {
1551
+ return OUTPUT_STYLES;
1469
1552
  }
1470
- function getPersonalityInfo(personalityId) {
1471
- return AI_PERSONALITIES.find((p) => p.id === personalityId);
1472
- }
1473
- async function configureAiPersonality(scriptLang, preselectedPersonality) {
1474
- const showExisting = typeof preselectedPersonality === "boolean" ? preselectedPersonality : true;
1475
- const preselected = typeof preselectedPersonality === "string" ? preselectedPersonality : void 0;
1476
- const i18n = getTranslation(scriptLang);
1477
- const existingPersonality = getExistingPersonality();
1478
- if (preselected) {
1479
- let directive2 = "";
1480
- if (preselected === "custom") {
1481
- directive2 = "You are a helpful assistant.";
1482
- } else {
1483
- const selected = AI_PERSONALITIES.find((p) => p.id === preselected);
1484
- if (selected) {
1485
- directive2 = selected.directive[scriptLang];
1486
- } else {
1487
- console.error(ansis.red(`Invalid personality: ${preselected}`));
1488
- return;
1489
- }
1553
+ async function copyOutputStyles(selectedStyles, lang) {
1554
+ const outputStylesDir = join(CLAUDE_DIR, "output-styles");
1555
+ ensureDir(outputStylesDir);
1556
+ const currentFilePath = fileURLToPath(import.meta.url);
1557
+ const distDir = dirname(dirname(currentFilePath));
1558
+ const rootDir = dirname(distDir);
1559
+ const templateDir = join(rootDir, "templates", lang, "output-styles");
1560
+ for (const styleId of selectedStyles) {
1561
+ const style = OUTPUT_STYLES.find((s) => s.id === styleId);
1562
+ if (!style || !style.isCustom || !style.filePath) {
1563
+ continue;
1490
1564
  }
1491
- await applyPersonalityDirective(directive2);
1492
- updateZcfConfig({ aiPersonality: preselected });
1493
- console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
1494
- return;
1495
- }
1496
- if (showExisting && existingPersonality) {
1497
- const personalityInfo = getPersonalityInfo(existingPersonality);
1498
- if (personalityInfo) {
1499
- console.log(`
1500
- ${ansis.blue(`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`)}`);
1501
- console.log(
1502
- ansis.gray(` ${i18n.configuration.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
1503
- );
1504
- const { modify } = await inquirer.prompt({
1505
- type: "confirm",
1506
- name: "modify",
1507
- message: i18n.configuration.modifyPersonality || "Modify AI personality?",
1508
- default: false
1509
- });
1510
- if (!modify) {
1511
- console.log(ansis.green(`\u2714 ${i18n.configuration.keepPersonality || "Keeping existing personality"}`));
1512
- return;
1513
- }
1565
+ const sourcePath = join(templateDir, style.filePath);
1566
+ const destPath = join(outputStylesDir, style.filePath);
1567
+ if (exists(sourcePath)) {
1568
+ copyFile(sourcePath, destPath);
1514
1569
  }
1515
1570
  }
1516
- const { personality } = await inquirer.prompt({
1517
- type: "list",
1518
- name: "personality",
1519
- message: i18n.configuration.selectAiPersonality || "Select AI personality",
1520
- choices: addNumbersToChoices(AI_PERSONALITIES.map((p) => ({
1521
- name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(`${p.directive[scriptLang].substring(0, 50)}...`)}` : `${p.name[scriptLang]} - ${ansis.gray(i18n.configuration.customPersonalityHint || "Define your own personality")}`,
1522
- value: p.id,
1523
- short: p.name[scriptLang]
1524
- }))),
1525
- default: existingPersonality ? AI_PERSONALITIES.findIndex((p) => p.id === existingPersonality) : 0
1571
+ }
1572
+ function setGlobalDefaultOutputStyle(styleId) {
1573
+ const existingSettings = readJsonConfig(SETTINGS_FILE) || {};
1574
+ const updatedSettings = {
1575
+ ...existingSettings,
1576
+ outputStyle: styleId
1577
+ };
1578
+ writeJsonConfig(SETTINGS_FILE, updatedSettings);
1579
+ }
1580
+ function hasLegacyPersonalityFiles() {
1581
+ return LEGACY_FILES.some((filename) => exists(join(CLAUDE_DIR, filename)));
1582
+ }
1583
+ function cleanupLegacyPersonalityFiles() {
1584
+ LEGACY_FILES.forEach((filename) => {
1585
+ const filePath = join(CLAUDE_DIR, filename);
1586
+ if (exists(filePath)) {
1587
+ removeFile(filePath);
1588
+ }
1526
1589
  });
1527
- if (!personality) {
1528
- console.log(ansis.yellow(i18n.common.cancelled));
1529
- return;
1530
- }
1531
- let directive = "";
1532
- if (personality === "custom") {
1533
- const { customDirective } = await inquirer.prompt({
1534
- type: "input",
1535
- name: "customDirective",
1536
- message: i18n.configuration.enterCustomPersonality || "Enter custom personality directive",
1537
- validate: (value) => !!value || i18n.configuration.directiveCannotBeEmpty
1590
+ }
1591
+ async function configureOutputStyle(displayLang, configLang, preselectedStyles, preselectedDefault) {
1592
+ const i18n = getTranslation(displayLang);
1593
+ const availableStyles = getAvailableOutputStyles();
1594
+ if (hasLegacyPersonalityFiles() && !preselectedStyles) {
1595
+ console.log(ansis.yellow(`\u26A0\uFE0F ${i18n.configuration.legacyFilesDetected}`));
1596
+ const { cleanupLegacy } = await inquirer.prompt({
1597
+ type: "confirm",
1598
+ name: "cleanupLegacy",
1599
+ message: i18n.configuration.cleanupLegacyFiles,
1600
+ default: true
1538
1601
  });
1539
- if (!customDirective) {
1602
+ if (cleanupLegacy) {
1603
+ cleanupLegacyPersonalityFiles();
1604
+ console.log(ansis.green(`\u2714 ${i18n.configuration.legacyFilesRemoved}`));
1605
+ }
1606
+ } else if (hasLegacyPersonalityFiles() && preselectedStyles) {
1607
+ cleanupLegacyPersonalityFiles();
1608
+ }
1609
+ let selectedStyles;
1610
+ let defaultStyle;
1611
+ if (preselectedStyles && preselectedDefault) {
1612
+ selectedStyles = preselectedStyles;
1613
+ defaultStyle = preselectedDefault;
1614
+ } else {
1615
+ const customStyles = availableStyles.filter((style) => style.isCustom);
1616
+ const { selectedStyles: promptedStyles } = await inquirer.prompt({
1617
+ type: "checkbox",
1618
+ name: "selectedStyles",
1619
+ message: `${i18n.configuration.selectOutputStyles}${i18n.common.multiSelectHint}`,
1620
+ choices: addNumbersToChoices(customStyles.map((style) => ({
1621
+ name: `${i18n.configuration.outputStyles[style.id]?.name || style.id} - ${ansis.gray(i18n.configuration.outputStyles[style.id]?.description || "")}`,
1622
+ value: style.id,
1623
+ checked: true
1624
+ // Default select all custom styles
1625
+ }))),
1626
+ validate: (input) => input.length > 0 || i18n.configuration.selectAtLeastOne
1627
+ });
1628
+ if (!promptedStyles || promptedStyles.length === 0) {
1540
1629
  console.log(ansis.yellow(i18n.common.cancelled));
1541
1630
  return;
1542
1631
  }
1543
- directive = customDirective;
1544
- } else {
1545
- const selected = AI_PERSONALITIES.find((p) => p.id === personality);
1546
- if (selected) {
1547
- directive = selected.directive[scriptLang];
1632
+ selectedStyles = promptedStyles;
1633
+ const { defaultStyle: promptedDefault } = await inquirer.prompt({
1634
+ type: "list",
1635
+ name: "defaultStyle",
1636
+ message: i18n.configuration.selectDefaultOutputStyle,
1637
+ choices: addNumbersToChoices([
1638
+ // Show selected custom styles first (only what user actually installed)
1639
+ ...selectedStyles.map((styleId) => {
1640
+ return {
1641
+ name: `${i18n.configuration.outputStyles[styleId]?.name || styleId} - ${ansis.gray(i18n.configuration.outputStyles[styleId]?.description || "")}`,
1642
+ value: styleId,
1643
+ short: i18n.configuration.outputStyles[styleId]?.name || styleId
1644
+ };
1645
+ }),
1646
+ // Then show all built-in styles (always available)
1647
+ ...availableStyles.filter((style) => !style.isCustom).map((style) => ({
1648
+ name: `${i18n.configuration.outputStyles[style.id]?.name || style.id} - ${ansis.gray(i18n.configuration.outputStyles[style.id]?.description || "")}`,
1649
+ value: style.id,
1650
+ short: i18n.configuration.outputStyles[style.id]?.name || style.id
1651
+ }))
1652
+ ]),
1653
+ default: selectedStyles.includes("engineer-professional") ? "engineer-professional" : selectedStyles[0]
1654
+ });
1655
+ if (!promptedDefault) {
1656
+ console.log(ansis.yellow(i18n.common.cancelled));
1657
+ return;
1548
1658
  }
1659
+ defaultStyle = promptedDefault;
1549
1660
  }
1550
- await applyPersonalityDirective(directive);
1551
- updateZcfConfig({ aiPersonality: personality });
1552
- console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
1553
- }
1554
- async function applyPersonalityDirective(directive) {
1555
- try {
1556
- const personalityFile = join(CLAUDE_DIR, "personality.md");
1557
- writeFile(personalityFile, directive);
1558
- } catch (error) {
1559
- const lang = readZcfConfig()?.preferredLang || "en";
1560
- const errorI18n = getTranslation(lang);
1561
- console.error(ansis.red(errorI18n.configuration.failedToApplyPersonality || "Failed to apply personality"), error);
1562
- }
1661
+ await copyOutputStyles(selectedStyles, configLang);
1662
+ setGlobalDefaultOutputStyle(defaultStyle);
1663
+ updateZcfConfig({
1664
+ outputStyles: selectedStyles,
1665
+ defaultOutputStyle: defaultStyle
1666
+ });
1667
+ console.log(ansis.green(`\u2714 ${i18n.configuration.outputStyleInstalled}`));
1668
+ console.log(ansis.gray(` ${i18n.configuration.selectedStyles}: ${selectedStyles.join(", ")}`));
1669
+ console.log(ansis.gray(` ${i18n.configuration.defaultStyle}: ${defaultStyle}`));
1563
1670
  }
1564
1671
 
1565
1672
  function displayBanner(subtitle) {
@@ -1836,39 +1943,18 @@ function backupExistingConfig() {
1836
1943
  copyDir(CLAUDE_DIR, backupDir, { filter });
1837
1944
  return backupDir;
1838
1945
  }
1839
- function copyConfigFiles(lang, onlyMd = false) {
1946
+ function copyConfigFiles(onlyMd = false) {
1840
1947
  const currentFilePath = fileURLToPath(import.meta.url);
1841
1948
  const distDir = dirname(dirname(currentFilePath));
1842
1949
  const rootDir = dirname(distDir);
1843
1950
  const baseTemplateDir = join(rootDir, "templates");
1844
- copyClaudeMemoryFiles(lang, rootDir);
1845
1951
  if (!onlyMd) {
1846
- const baseSettingsPath = join(baseTemplateDir, "settings.json");
1952
+ const baseSettingsPath = join(baseTemplateDir, "common", "settings.json");
1847
1953
  const destSettingsPath = join(CLAUDE_DIR, "settings.json");
1848
1954
  if (exists(baseSettingsPath)) {
1849
1955
  mergeSettingsFile(baseSettingsPath, destSettingsPath);
1850
1956
  }
1851
1957
  }
1852
- const claudeMdSource = join(baseTemplateDir, "common", "CLAUDE.md");
1853
- const claudeMdDest = join(CLAUDE_DIR, "CLAUDE.md");
1854
- if (exists(claudeMdSource)) {
1855
- copyFile(claudeMdSource, claudeMdDest);
1856
- }
1857
- }
1858
- function copyClaudeMemoryFiles(lang, rootDir) {
1859
- const memorySourceDir = join(rootDir, "templates", lang, "memory");
1860
- if (!exists(memorySourceDir)) {
1861
- const i18n = getTranslation(lang);
1862
- throw new Error(`${i18n.configuration.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
1863
- }
1864
- const files = readDir(memorySourceDir);
1865
- files?.forEach((file) => {
1866
- if (file.endsWith(".md")) {
1867
- const sourcePath = join(memorySourceDir, file);
1868
- const destPath = join(CLAUDE_DIR, file);
1869
- copyFile(sourcePath, destPath);
1870
- }
1871
- });
1872
1958
  }
1873
1959
  function getDefaultSettings() {
1874
1960
  try {
@@ -2016,7 +2102,7 @@ function getExistingApiConfig() {
2016
2102
  };
2017
2103
  }
2018
2104
  function applyAiLanguageDirective(aiOutputLang) {
2019
- const languageFile = join(CLAUDE_DIR, "language.md");
2105
+ const claudeFile = join(CLAUDE_DIR, "CLAUDE.md");
2020
2106
  let directive = "";
2021
2107
  if (aiOutputLang === "custom") {
2022
2108
  return;
@@ -2025,7 +2111,7 @@ function applyAiLanguageDirective(aiOutputLang) {
2025
2111
  } else {
2026
2112
  directive = `Always respond in ${aiOutputLang}`;
2027
2113
  }
2028
- writeFile(languageFile, directive);
2114
+ writeFile(claudeFile, directive);
2029
2115
  }
2030
2116
 
2031
2117
  const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
@@ -3015,11 +3101,15 @@ async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
3015
3101
  if (backupDir) {
3016
3102
  console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
3017
3103
  }
3018
- copyConfigFiles(configLang, true);
3019
3104
  if (aiOutputLang) {
3020
3105
  applyAiLanguageDirective(aiOutputLang);
3021
3106
  }
3022
- await configureAiPersonality(scriptLang);
3107
+ await configureOutputStyle(
3108
+ scriptLang,
3109
+ // Display language for UI
3110
+ configLang
3111
+ // Config language for templates
3112
+ );
3023
3113
  console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
3024
3114
  console.log(`
3025
3115
  ${ansis.cyan(i18n.common.complete)}`);
@@ -3361,8 +3451,20 @@ function validateSkipPromptOptions(options) {
3361
3451
  if (!options.aiOutputLang) {
3362
3452
  options.aiOutputLang = "en";
3363
3453
  }
3364
- if (!options.aiPersonality) {
3365
- options.aiPersonality = "professional";
3454
+ if (typeof options.outputStyles === "string") {
3455
+ if (options.outputStyles === "skip") {
3456
+ options.outputStyles = false;
3457
+ } else if (options.outputStyles === "all") {
3458
+ options.outputStyles = ["engineer-professional", "nekomata-engineer", "laowang-engineer"];
3459
+ } else {
3460
+ options.outputStyles = options.outputStyles.split(",").map((s) => s.trim());
3461
+ }
3462
+ }
3463
+ if (options.outputStyles === void 0) {
3464
+ options.outputStyles = ["engineer-professional", "nekomata-engineer", "laowang-engineer"];
3465
+ }
3466
+ if (!options.defaultOutputStyle) {
3467
+ options.defaultOutputStyle = "engineer-professional";
3366
3468
  }
3367
3469
  if (typeof options.installCometixLine === "string") {
3368
3470
  options.installCometixLine = options.installCometixLine.toLowerCase() === "true";
@@ -3403,6 +3505,20 @@ function validateSkipPromptOptions(options) {
3403
3505
  }
3404
3506
  }
3405
3507
  }
3508
+ if (Array.isArray(options.outputStyles)) {
3509
+ const validStyles = ["engineer-professional", "nekomata-engineer", "laowang-engineer", "default", "explanatory", "learning"];
3510
+ for (const style of options.outputStyles) {
3511
+ if (!validStyles.includes(style)) {
3512
+ throw new Error(`Invalid output style: ${style}. Available styles: ${validStyles.join(", ")}`);
3513
+ }
3514
+ }
3515
+ }
3516
+ if (options.defaultOutputStyle) {
3517
+ const validStyles = ["engineer-professional", "nekomata-engineer", "laowang-engineer", "default", "explanatory", "learning"];
3518
+ if (!validStyles.includes(options.defaultOutputStyle)) {
3519
+ throw new Error(`Invalid default output style: ${options.defaultOutputStyle}. Available styles: ${validStyles.join(", ")}`);
3520
+ }
3521
+ }
3406
3522
  if (typeof options.workflows === "string") {
3407
3523
  if (options.workflows === "skip") {
3408
3524
  options.workflows = false;
@@ -3681,7 +3797,7 @@ ${ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`)}`);
3681
3797
  }
3682
3798
  }
3683
3799
  if (action === "docs-only") {
3684
- copyConfigFiles(configLang, true);
3800
+ copyConfigFiles(true);
3685
3801
  if (options.skipPrompt) {
3686
3802
  if (options.workflows !== false) {
3687
3803
  await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
@@ -3690,7 +3806,7 @@ ${ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`)}`);
3690
3806
  await selectAndInstallWorkflows(configLang, scriptLang);
3691
3807
  }
3692
3808
  } else if (["backup", "merge", "new"].includes(action)) {
3693
- copyConfigFiles(configLang, false);
3809
+ copyConfigFiles(false);
3694
3810
  if (options.skipPrompt) {
3695
3811
  if (options.workflows !== false) {
3696
3812
  await selectAndInstallWorkflows(configLang, scriptLang, options.workflows);
@@ -3701,9 +3817,23 @@ ${ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`)}`);
3701
3817
  }
3702
3818
  applyAiLanguageDirective(aiOutputLang);
3703
3819
  if (options.skipPrompt) {
3704
- await configureAiPersonality(scriptLang, options.aiPersonality);
3820
+ if (options.outputStyles !== false) {
3821
+ await configureOutputStyle(
3822
+ scriptLang,
3823
+ // Display language for UI
3824
+ configLang,
3825
+ // Config language for templates
3826
+ options.outputStyles,
3827
+ options.defaultOutputStyle
3828
+ );
3829
+ }
3705
3830
  } else {
3706
- await configureAiPersonality(scriptLang);
3831
+ await configureOutputStyle(
3832
+ scriptLang,
3833
+ // Display language for UI
3834
+ configLang
3835
+ // Config language for templates
3836
+ );
3707
3837
  }
3708
3838
  if (apiConfig && action !== "docs-only") {
3709
3839
  const configuredApi = configureApi(apiConfig);
@@ -3905,4 +4035,4 @@ async function openSettingsJson() {
3905
4035
  }
3906
4036
  }
3907
4037
 
3908
- export { modifyApiConfigPartially as $, AI_OUTPUT_LANGUAGES as A, writeMcpConfig as B, CLAUDE_DIR as C, backupMcpConfig as D, mergeMcpServers as E, buildMcpServerConfig as F, fixWindowsMcpConfig as G, addCompletedOnboarding as H, I18N as I, readCcrConfig as J, isCcrInstalled as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, installCcr as N, configureCcrFeature as O, handleExitPromptError as P, handleGeneralError as Q, getTranslation as R, SETTINGS_FILE as S, addNumbersToChoices as T, updateZcfConfig as U, readZcfConfig as V, configureAiPersonality as W, isWindows as X, selectMcpServices as Y, ZCF_CONFIG_FILE as Z, formatApiKeyDisplay as _, commandExists as a, setupCcrConfiguration as a0, validateApiKey as a1, readZcfConfigAsync as a2, COMETIX_COMMAND_NAME as a3, COMETIX_COMMANDS as a4, installCometixLine as a5, selectScriptLanguage as a6, checkAndUpdateTools as a7, displayBanner as a8, resolveAiOutputLanguage as a9, updatePromptOnly as aa, selectAndInstallWorkflows as ab, version as ac, displayBannerWithInfo as ad, prompts as ae, importRecommendedEnv as b, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, ensureClaudeDir as k, backupExistingConfig as l, mergeAndCleanPermissions as m, copyConfigFiles as n, openSettingsJson as o, configureApi as p, mergeConfigs as q, mergeSettingsFile as r, getExistingModelConfig as s, getExistingApiConfig as t, updateDefaultModel as u, applyAiLanguageDirective as v, isClaudeCodeInstalled as w, installClaudeCode as x, getMcpConfigPath as y, readMcpConfig as z };
4038
+ export { modifyApiConfigPartially as $, AI_OUTPUT_LANGUAGES as A, writeMcpConfig as B, CLAUDE_DIR as C, backupMcpConfig as D, mergeMcpServers as E, buildMcpServerConfig as F, fixWindowsMcpConfig as G, addCompletedOnboarding as H, I18N as I, readCcrConfig as J, isCcrInstalled as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, installCcr as N, configureCcrFeature as O, handleExitPromptError as P, handleGeneralError as Q, getTranslation as R, SETTINGS_FILE as S, addNumbersToChoices as T, updateZcfConfig as U, readZcfConfig as V, configureOutputStyle as W, isWindows as X, selectMcpServices as Y, ZCF_CONFIG_FILE as Z, formatApiKeyDisplay as _, commandExists as a, setupCcrConfiguration as a0, validateApiKey as a1, readZcfConfigAsync as a2, COMETIX_COMMAND_NAME as a3, COMETIX_COMMANDS as a4, installCometixLine as a5, selectScriptLanguage as a6, checkAndUpdateTools as a7, displayBanner as a8, resolveAiOutputLanguage as a9, updatePromptOnly as aa, selectAndInstallWorkflows as ab, version as ac, displayBannerWithInfo as ad, prompts as ae, importRecommendedEnv as b, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, ensureClaudeDir as k, backupExistingConfig as l, mergeAndCleanPermissions as m, copyConfigFiles as n, openSettingsJson as o, configureApi as p, mergeConfigs as q, mergeSettingsFile as r, getExistingModelConfig as s, getExistingApiConfig as t, updateDefaultModel as u, applyAiLanguageDirective as v, isClaudeCodeInstalled as w, installClaudeCode as x, getMcpConfigPath as y, readMcpConfig as z };