@aigne/doc-smith 0.2.11 → 0.3.1

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.
@@ -9,6 +9,8 @@ export const DEFAULT_INCLUDE_PATTERNS = [
9
9
  "*.jsx",
10
10
  "*.ts",
11
11
  "*.tsx",
12
+ "*.mjs",
13
+ "*.mts",
12
14
  // C/C++
13
15
  "*.c",
14
16
  "*.cc",
@@ -87,16 +89,16 @@ export const DEFAULT_INCLUDE_PATTERNS = [
87
89
  ];
88
90
 
89
91
  export const DEFAULT_EXCLUDE_PATTERNS = [
90
- "aigne-docs/**",
91
- "doc-smith/**",
92
- ".aigne/**",
93
- "assets/**",
94
- "data/**",
95
- "images/**",
96
- "public/**",
97
- "static/**",
92
+ "**/aigne-docs/**",
93
+ "**/doc-smith/**",
94
+ "**/.aigne/**",
95
+ "**/assets/**",
96
+ "**/data/**",
97
+ "**/images/**",
98
+ "**/public/**",
99
+ "**/static/**",
98
100
  "**/vendor/**",
99
- "temp/**",
101
+ "**/temp/**",
100
102
  "**/*docs/**",
101
103
  "**/*doc/**",
102
104
  "**/*venv/**",
@@ -122,6 +124,13 @@ export const DEFAULT_EXCLUDE_PATTERNS = [
122
124
  "**/*node_modules/**",
123
125
  "*.log",
124
126
  "**/*test.*",
127
+ "**/pnpm-lock.yaml",
128
+ "**/yarn.lock",
129
+ "**/package-lock.json",
130
+ "**/pnpm-lock.json",
131
+ "**/bun.lockb",
132
+ "**/bun.lock",
133
+ "**/bun.lockb",
125
134
  ];
126
135
 
127
136
  // Supported languages for documentation
@@ -140,33 +149,385 @@ export const SUPPORTED_LANGUAGES = [
140
149
  { code: "ar", label: "العربية (ar)", sample: "مرحبا" },
141
150
  ];
142
151
 
143
- // Predefined document generation styles
152
+ // Predefined document generation styles based on primary purpose
144
153
  export const DOCUMENT_STYLES = {
145
- userGuide: {
146
- name: "User Guide",
147
- rules: "Scenario-based; step-by-step; plain language; outcomes & cautions.",
154
+ getStarted: {
155
+ name: "Get started quickly",
156
+ description: "Help new users go from zero to working in <30 minutes",
157
+ content:
158
+ "Optimizes for: Speed, essential steps, working examples.\nSkips: Complex edge cases, theoretical background.",
148
159
  },
149
- developerDocs: {
150
- name: "Developer Docs",
151
- rules: "Steps-first; copy-paste examples; minimal context; active 'you'.",
160
+ completeTasks: {
161
+ name: "Complete specific tasks",
162
+ description: "Guide users through common workflows and use cases",
163
+ content:
164
+ "Optimizes for: Step-by-step instructions, practical scenarios.\nSkips: Deep technical internals, getting started basics.",
152
165
  },
153
- apiReference: {
154
- name: "API Reference",
155
- rules: "Exact & skimmable; schema-first; clear params/errors/examples.",
166
+ findAnswers: {
167
+ name: "Find answers fast",
168
+ description: "Provide searchable reference for all features and APIs",
169
+ content:
170
+ "Optimizes for: Comprehensive coverage, searchability, examples.\n Skips: Narrative flow, beginner explanations.",
156
171
  },
157
- custom: {
158
- name: "Custom Rules",
159
- rules: "Enter your own documentation generation rules",
172
+ understandSystem: {
173
+ name: "Understand the system",
174
+ description: "Explain how it works, why design decisions were made",
175
+ content:
176
+ "Optimizes for: Architecture, concepts, decision rationale.\nSkips: Quick wins, copy-paste solutions.",
177
+ },
178
+ solveProblems: {
179
+ name: "Solve problems",
180
+ description: "Help users troubleshoot and fix issues",
181
+ content:
182
+ "Optimizes for: Error scenarios, diagnostics, solutions.\nSkips: Happy path tutorials, feature overviews.",
183
+ },
184
+ mixedPurpose: {
185
+ name: "Mix of above",
186
+ description: "Comprehensive documentation covering multiple needs",
187
+ content:
188
+ "Optimizes for: Balanced coverage, multiple entry points.\nTrade-off: Longer, requires good navigation.",
160
189
  },
161
190
  };
162
191
 
163
- // Predefined target audiences
192
+ // Predefined target audiences based on who will read the documentation most often
164
193
  export const TARGET_AUDIENCES = {
165
- generalUsers: "General Users",
166
- actionFirst: "Developers, Implementation Engineers, DevOps",
167
- conceptFirst: "Architects, Technical Leads, Developers interested in principles",
168
- custom: "Enter your own target audience",
194
+ endUsers: {
195
+ name: "End users (non-technical)",
196
+ description: "People who use the product but don't code",
197
+ content:
198
+ "Writing: Plain language, UI-focused, avoid technical terms.\nExamples: Screenshots, step-by-step clicks, business outcomes.",
199
+ },
200
+ developers: {
201
+ name: "Developers integrating",
202
+ description: "Engineers adding this to their projects",
203
+ content:
204
+ "Writing: Code-first, copy-paste ready, technical accuracy.\nExamples: SDK usage, API calls, configuration snippets.",
205
+ },
206
+ devops: {
207
+ name: "DevOps/Infrastructure",
208
+ description: "Teams deploying, monitoring, maintaining systems",
209
+ content:
210
+ "Writing: Operations-focused, troubleshooting emphasis.\nExamples: Deployment configs, monitoring setup, scaling guides.",
211
+ },
212
+ decisionMakers: {
213
+ name: "Technical decision makers",
214
+ description: "Architects, leads evaluating or planning implementation",
215
+ content:
216
+ "Writing: High-level first, drill-down details available.\nExamples: Architecture diagrams, comparison tables, trade-offs.",
217
+ },
218
+ supportTeams: {
219
+ name: "Support teams",
220
+ description: "People helping others use the product",
221
+ content:
222
+ "Writing: Diagnostic-focused, common issues prominent.\nExamples: Troubleshooting flows, FAQ format, escalation paths.",
223
+ },
224
+ mixedTechnical: {
225
+ name: "Mixed technical audience",
226
+ description: "Developers, DevOps, and technical users",
227
+ content:
228
+ "Writing: Layered complexity, multiple entry points.\nExamples: Progressive disclosure, role-based navigation.",
229
+ },
230
+ };
231
+
232
+ // Reader knowledge level - what do readers typically know when they arrive?
233
+ export const READER_KNOWLEDGE_LEVELS = {
234
+ completeBeginners: {
235
+ name: "Complete beginners",
236
+ description: "New to this domain/technology entirely",
237
+ content:
238
+ "Content: Define terms, explain concepts, assume nothing.\nStructure: Linear progression, prerequisite checks.\nTone: Patient, educational, confidence-building.",
239
+ },
240
+ domainFamiliar: {
241
+ name: "Domain-familiar, tool-new",
242
+ description: "Know the problem space, new to this specific solution",
243
+ content:
244
+ 'Content: Focus on differences, migration, unique features.\nStructure: Comparison-heavy, "coming from X" sections.\nTone: Efficient, highlight advantages, skip basics.',
245
+ },
246
+ experiencedUsers: {
247
+ name: "Experienced users",
248
+ description: "Regular users needing reference/advanced topics",
249
+ content:
250
+ "Content: Dense information, edge cases, performance tips.\nStructure: Reference-style, searchable, task-oriented.\nTone: Concise, technical precision, assume competence.",
251
+ },
252
+ emergencyTroubleshooting: {
253
+ name: "Emergency/troubleshooting",
254
+ description: "Something's broken, need to fix it quickly",
255
+ content:
256
+ "Content: Symptom → diagnosis → solution format.\nStructure: Problem-first, solution-prominent, escalation paths.\nTone: Clear, actionable, confidence-inspiring.",
257
+ },
258
+ exploringEvaluating: {
259
+ name: "Exploring/evaluating",
260
+ description: "Trying to understand if this fits their needs",
261
+ content:
262
+ "Content: Use cases, comparisons, getting started quickly.\nStructure: Multiple entry points, progressive commitment.\nTone: Persuasive but honest, show value quickly.",
263
+ },
264
+ };
265
+
266
+ // Documentation depth - how comprehensive should the documentation be?
267
+ export const DOCUMENTATION_DEPTH = {
268
+ essentialOnly: {
269
+ name: "Essential only",
270
+ description: "Cover the 80% use cases, keep it concise",
271
+ content:
272
+ "Scope: Core features, happy paths, common patterns.\nLength: Shorter sections, key examples only.\nMaintenance: Easier to keep current.",
273
+ },
274
+ balancedCoverage: {
275
+ name: "Balanced coverage",
276
+ description: "Good depth with practical examples [RECOMMENDED]",
277
+ content:
278
+ "Scope: Most features, some edge cases, multiple examples.\nLength: Moderate sections, varied example types.\nMaintenance: Reasonable update burden.",
279
+ },
280
+ comprehensive: {
281
+ name: "Comprehensive",
282
+ description: "Cover all features, edge cases, and advanced scenarios",
283
+ content:
284
+ "Scope: Everything, all parameters, extensive examples.\nLength: Detailed sections, comprehensive coverage.\nMaintenance: Higher update complexity.",
285
+ },
286
+ aiDecide: {
287
+ name: "Let AI decide",
288
+ description: "Analyze code complexity and suggest appropriate depth",
289
+ content:
290
+ "Scope: Adaptive based on codebase analysis.\nAI considers: API surface area, complexity, existing patterns.",
291
+ },
292
+ };
293
+
294
+ // Purpose to Knowledge Level mapping for default suggestions
295
+ export const PURPOSE_TO_KNOWLEDGE_MAPPING = {
296
+ getStarted: "completeBeginners", // Get started → Complete beginners
297
+ findAnswers: "experiencedUsers", // Find answers → Experienced users
298
+ solveProblems: "emergencyTroubleshooting", // Solve problems → Emergency/troubleshooting
299
+ exploringEvaluating: "exploringEvaluating", // Exploring → Exploring/evaluating
300
+ };
301
+
302
+ // Documentation Depth recommendation logic
303
+ export const DEPTH_RECOMMENDATION_LOGIC = {
304
+ // Purpose-based recommendations (highest priority)
305
+ purposes: {
306
+ getStarted: "essentialOnly", // Get started → Essential
307
+ },
308
+ // Audience-based recommendations (medium priority)
309
+ audiences: {
310
+ decisionMakers: "balancedCoverage", // Decision makers → Balanced
311
+ },
312
+ // Knowledge level-based recommendations (lowest priority)
313
+ knowledgeLevels: {
314
+ experiencedUsers: "comprehensive", // Experienced users → Comprehensive
315
+ },
169
316
  };
170
317
 
171
318
  // Component mount point ID for Discuss Kit
172
319
  export const DISCUSS_KIT_DID = "z8ia1WEiBZ7hxURf6LwH21Wpg99vophFwSJdu";
320
+
321
+ // Discuss Kit related URLs
322
+ export const DISCUSS_KIT_STORE_URL =
323
+ "https://store.blocklet.dev/blocklets/z8ia1WEiBZ7hxURf6LwH21Wpg99vophFwSJdu";
324
+ export const BLOCKLET_ADD_COMPONENT_DOCS =
325
+ "https://www.arcblock.io/docs/blocklet-developer/en/7zbw0GQXgcD6sCcjVfwqqT2s";
326
+
327
+ // Conflict rules configuration for documentation generation
328
+ export const CONFLICT_RULES = {
329
+ // Internal conflicts within the same question (multi-select conflicts)
330
+ internalConflicts: {
331
+ documentPurpose: [
332
+ {
333
+ conflictItems: ["getStarted", "findAnswers"],
334
+ severity: "severe",
335
+ reason:
336
+ "Quick start guide (skips complex cases) conflicts with comprehensive API reference (skips beginner explanations)",
337
+ suggestion: "Choose one as primary goal, or consider creating layered documentation",
338
+ },
339
+ {
340
+ conflictItems: ["getStarted", "understandSystem"],
341
+ severity: "severe",
342
+ reason: "30-minute quick start conflicts with deep architectural concept explanations",
343
+ suggestion: "Consider creating separate quick start and architecture design docs",
344
+ },
345
+ {
346
+ conflictItems: ["completeTasks", "understandSystem"],
347
+ severity: "moderate",
348
+ reason:
349
+ "Practical task guidance and theoretical concept explanation have different focuses",
350
+ suggestion:
351
+ "Can be handled through layered document structure: concepts first, then practice",
352
+ },
353
+ {
354
+ conflictItems: ["getStarted", "solveProblems"],
355
+ severity: "moderate",
356
+ reason:
357
+ "Quick start (success cases) and troubleshooting (error scenarios) have different focuses",
358
+ suggestion: "Create separate tutorial and troubleshooting guides",
359
+ },
360
+ {
361
+ conflictItems: ["findAnswers", "solveProblems"],
362
+ severity: "moderate",
363
+ reason: "API reference and diagnostic documentation have different organizational logic",
364
+ suggestion: "Add troubleshooting section to reference documentation",
365
+ },
366
+ ],
367
+ targetAudienceTypes: [
368
+ {
369
+ conflictItems: ["endUsers", "developers"],
370
+ severity: "severe",
371
+ reason:
372
+ "Non-technical users (avoid technical terms) conflict with developers (code-first approach)",
373
+ suggestion:
374
+ "Create separate documentation for different audiences or use layered content design",
375
+ },
376
+ {
377
+ conflictItems: ["endUsers", "devops"],
378
+ severity: "severe",
379
+ reason: "Non-technical users and operations technical personnel have very different needs",
380
+ suggestion: "Consider creating separate user guides and operations documentation",
381
+ },
382
+ {
383
+ conflictItems: ["endUsers", "decisionMakers"],
384
+ severity: "severe",
385
+ reason:
386
+ "Non-technical users (simple language) and decision makers (architecture diagrams) have different needs",
387
+ suggestion: "Create high-level overview for management and user operation manuals",
388
+ },
389
+ {
390
+ conflictItems: ["developers", "decisionMakers"],
391
+ severity: "moderate",
392
+ reason:
393
+ "Developers (code details) and decision makers (high-level overview) have different focus areas",
394
+ suggestion: "Use progressive disclosure: high-level first, then details",
395
+ },
396
+ {
397
+ conflictItems: ["supportTeams", "decisionMakers"],
398
+ severity: "moderate",
399
+ reason:
400
+ "Support teams (problem diagnosis) and decision makers (architecture decisions) have different focus areas",
401
+ suggestion: "Include operational considerations in decision documentation",
402
+ },
403
+ ],
404
+ },
405
+
406
+ // Cross-question conflicts (conflicts between different questions)
407
+ crossConflicts: [
408
+ {
409
+ conditions: {
410
+ documentPurpose: ["getStarted"],
411
+ readerKnowledgeLevel: ["experiencedUsers"],
412
+ },
413
+ severity: "severe",
414
+ reason:
415
+ "Quick start tutorials are not suitable for experienced users who need advanced features and best practices",
416
+ action: "filter",
417
+ conflictingOptions: {
418
+ readerKnowledgeLevel: ["experiencedUsers"],
419
+ },
420
+ },
421
+ {
422
+ conditions: {
423
+ documentPurpose: ["findAnswers"],
424
+ readerKnowledgeLevel: ["completeBeginners"],
425
+ },
426
+ severity: "severe",
427
+ reason:
428
+ "API reference documentation skips beginner explanations and is not suitable for complete beginners",
429
+ action: "filter",
430
+ conflictingOptions: {
431
+ readerKnowledgeLevel: ["completeBeginners"],
432
+ },
433
+ },
434
+ {
435
+ conditions: {
436
+ documentPurpose: ["understandSystem"],
437
+ readerKnowledgeLevel: ["emergencyTroubleshooting"],
438
+ },
439
+ severity: "severe",
440
+ reason:
441
+ "System architecture explanations are not suitable for emergency troubleshooting scenarios",
442
+ action: "filter",
443
+ conflictingOptions: {
444
+ readerKnowledgeLevel: ["emergencyTroubleshooting"],
445
+ },
446
+ },
447
+ {
448
+ conditions: {
449
+ targetAudienceTypes: ["endUsers"],
450
+ readerKnowledgeLevel: ["experiencedUsers"],
451
+ },
452
+ severity: "severe",
453
+ reason: "Non-technical users are typically not experienced technical users",
454
+ action: "filter",
455
+ conflictingOptions: {
456
+ readerKnowledgeLevel: ["experiencedUsers"],
457
+ },
458
+ },
459
+ {
460
+ conditions: {
461
+ targetAudienceTypes: ["decisionMakers"],
462
+ readerKnowledgeLevel: ["emergencyTroubleshooting"],
463
+ },
464
+ severity: "severe",
465
+ reason: "Decision makers typically do not directly handle emergency technical failures",
466
+ action: "filter",
467
+ conflictingOptions: {
468
+ readerKnowledgeLevel: ["emergencyTroubleshooting"],
469
+ },
470
+ },
471
+ {
472
+ conditions: {
473
+ documentPurpose: ["getStarted"],
474
+ documentationDepth: ["comprehensive"],
475
+ },
476
+ severity: "severe",
477
+ reason: "Quick start tutorials contradict comprehensive coverage documentation",
478
+ action: "filter",
479
+ conflictingOptions: {
480
+ documentationDepth: ["comprehensive"],
481
+ },
482
+ },
483
+ {
484
+ conditions: {
485
+ documentPurpose: ["solveProblems"],
486
+ documentationDepth: ["essentialOnly"],
487
+ },
488
+ severity: "moderate",
489
+ reason:
490
+ "Troubleshooting usually needs to cover edge cases, basic content alone may not be sufficient",
491
+ action: "filter",
492
+ conflictingOptions: {
493
+ documentationDepth: ["essentialOnly"],
494
+ },
495
+ },
496
+ {
497
+ conditions: {
498
+ targetAudienceTypes: ["endUsers"],
499
+ documentationDepth: ["comprehensive"],
500
+ },
501
+ severity: "moderate",
502
+ reason: "Non-technical users typically do not need comprehensive technical coverage",
503
+ action: "filter",
504
+ conflictingOptions: {
505
+ documentationDepth: ["comprehensive"],
506
+ },
507
+ },
508
+ {
509
+ conditions: {
510
+ targetAudienceTypes: ["decisionMakers"],
511
+ documentationDepth: ["essentialOnly"],
512
+ },
513
+ severity: "moderate",
514
+ reason: "Decision makers may need more comprehensive information to make decisions",
515
+ action: "filter",
516
+ conflictingOptions: {
517
+ documentationDepth: ["essentialOnly"],
518
+ },
519
+ },
520
+ {
521
+ conditions: {
522
+ readerKnowledgeLevel: ["completeBeginners"],
523
+ documentationDepth: ["essentialOnly"],
524
+ },
525
+ severity: "moderate",
526
+ reason: "Complete beginners may need more explanations, not just core content",
527
+ action: "filter",
528
+ conflictingOptions: {
529
+ documentationDepth: ["essentialOnly"],
530
+ },
531
+ },
532
+ ],
533
+ };
@@ -335,6 +335,20 @@ export async function checkMarkdown(markdown, source = "content", options = {})
335
335
  }
336
336
  }
337
337
 
338
+ // Check for numbered list format in node labels (for both [] and {} syntax)
339
+ const nodeLabelWithNumberRegex =
340
+ /[A-Za-z0-9_]+\["([^"]*\d+\.\s[^"]*)"\]|[A-Za-z0-9_]+{"([^}]*\d+\.\s[^}]*)"}/g;
341
+ let numberMatch;
342
+ while ((numberMatch = nodeLabelWithNumberRegex.exec(mermaidContent)) !== null) {
343
+ const label = numberMatch[1] || numberMatch[2];
344
+ // Check if the label contains numbered list format
345
+ if (/\d+\.\s/.test(label)) {
346
+ errorMessages.push(
347
+ `Unsupported markdown: list - Found numbered list format in Mermaid node label in ${source} at line ${line}: "${label}" - numbered lists in node labels cause rendering issues`,
348
+ );
349
+ }
350
+ }
351
+
338
352
  // Check for special characters in node labels that should be quoted
339
353
  const nodeWithSpecialCharsRegex = /([A-Za-z0-9_]+)\[([^\]]*[(){}:;,\-\s.][^\]]*)\]/g;
340
354
  let specialCharMatch;
package/utils/utils.mjs CHANGED
@@ -2,9 +2,16 @@ import { execSync } from "node:child_process";
2
2
  import { accessSync, constants, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
- import chalk from "chalk";
6
5
  import { parse } from "yaml";
7
- import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_INCLUDE_PATTERNS } from "./constants.mjs";
6
+ import {
7
+ DEFAULT_EXCLUDE_PATTERNS,
8
+ DEFAULT_INCLUDE_PATTERNS,
9
+ DOCUMENT_STYLES,
10
+ TARGET_AUDIENCES,
11
+ READER_KNOWLEDGE_LEVELS,
12
+ DOCUMENTATION_DEPTH,
13
+ SUPPORTED_LANGUAGES,
14
+ } from "./constants.mjs";
8
15
 
9
16
  /**
10
17
  * Normalize path to absolute path for consistent comparison
@@ -94,7 +101,6 @@ export async function saveDocWithTranslations({
94
101
 
95
102
  await fs.writeFile(mainFilePath, finalContent, "utf8");
96
103
  results.push({ path: mainFilePath, success: true });
97
- console.log(chalk.green(`Saved: ${chalk.cyan(mainFilePath)}`));
98
104
  }
99
105
 
100
106
  // Process all translations
@@ -113,7 +119,6 @@ export async function saveDocWithTranslations({
113
119
 
114
120
  await fs.writeFile(translatePath, finalTranslationContent, "utf8");
115
121
  results.push({ path: translatePath, success: true });
116
- console.log(chalk.green(`Saved: ${chalk.cyan(translatePath)}`));
117
122
  }
118
123
  } catch (err) {
119
124
  results.push({ path: docPath, success: false, error: err.message });
@@ -703,3 +708,144 @@ export async function getProjectInfo() {
703
708
  fromGitHub,
704
709
  };
705
710
  }
711
+
712
+ /**
713
+ * Process configuration fields - convert keys to actual content
714
+ * @param {Object} config - Parsed configuration
715
+ * @returns {Object} Processed configuration with content fields
716
+ */
717
+ export function processConfigFields(config) {
718
+ const processed = {};
719
+ const allRulesContent = [];
720
+
721
+ // Check if original rules field has content
722
+ const existingRules = config.rules?.trim();
723
+ if (existingRules) {
724
+ allRulesContent.push(existingRules);
725
+ }
726
+
727
+ // Process document purpose (array)
728
+ let purposeContents = "";
729
+ if (config.documentPurpose && Array.isArray(config.documentPurpose)) {
730
+ purposeContents = config.documentPurpose
731
+ .map((key) => DOCUMENT_STYLES[key]?.content)
732
+ .filter(Boolean)
733
+ .join("\n\n");
734
+
735
+ if (purposeContents) {
736
+ allRulesContent.push(purposeContents);
737
+ }
738
+ }
739
+
740
+ // Process target audience types (array)
741
+ let audienceContents = "";
742
+ let audienceNames = "";
743
+ if (config.targetAudienceTypes && Array.isArray(config.targetAudienceTypes)) {
744
+ // Get content for rules
745
+ audienceContents = config.targetAudienceTypes
746
+ .map((key) => TARGET_AUDIENCES[key]?.content)
747
+ .filter(Boolean)
748
+ .join("\n\n");
749
+
750
+ // Get names for targetAudience field
751
+ audienceNames = config.targetAudienceTypes
752
+ .map((key) => TARGET_AUDIENCES[key]?.name)
753
+ .filter(Boolean)
754
+ .join(", ");
755
+
756
+ if (audienceContents) {
757
+ allRulesContent.push(audienceContents);
758
+ }
759
+
760
+ if (audienceNames) {
761
+ // Check if original targetAudience field has content
762
+ const existingTargetAudience = config.targetAudience?.trim();
763
+ const newAudienceNames = audienceNames;
764
+
765
+ if (existingTargetAudience) {
766
+ processed.targetAudience = `${existingTargetAudience}\n\n${newAudienceNames}`;
767
+ } else {
768
+ processed.targetAudience = newAudienceNames;
769
+ }
770
+ }
771
+ }
772
+
773
+ // Process reader knowledge level (single value)
774
+ let knowledgeContent = "";
775
+ if (config.readerKnowledgeLevel) {
776
+ knowledgeContent = READER_KNOWLEDGE_LEVELS[config.readerKnowledgeLevel]?.content;
777
+ if (knowledgeContent) {
778
+ processed.readerKnowledgeContent = knowledgeContent;
779
+ allRulesContent.push(`Reader Knowledge Level:\n${knowledgeContent}`);
780
+ }
781
+ }
782
+
783
+ // Process documentation depth (single value)
784
+ let depthContent = "";
785
+ if (config.documentationDepth) {
786
+ depthContent = DOCUMENTATION_DEPTH[config.documentationDepth]?.content;
787
+ if (depthContent) {
788
+ processed.documentationDepthContent = depthContent;
789
+ allRulesContent.push(`Documentation Depth:\n${depthContent}`);
790
+ }
791
+ }
792
+
793
+ // Combine all content into rules field
794
+ if (allRulesContent.length > 0) {
795
+ processed.rules = allRulesContent.join("\n\n");
796
+ }
797
+
798
+ return processed;
799
+ }
800
+
801
+ /**
802
+ * Detect system language and map to supported language code
803
+ * @returns {string} - Supported language code (defaults to 'en' if detection fails or unsupported)
804
+ */
805
+ export function detectSystemLanguage() {
806
+ try {
807
+ // Try multiple methods to detect system language
808
+ let systemLocale = null;
809
+
810
+ // Method 1: Environment variables (most reliable on Unix systems)
811
+ systemLocale = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL;
812
+
813
+ // Method 2: Node.js Intl API (fallback)
814
+ if (!systemLocale) {
815
+ try {
816
+ systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
817
+ } catch (_error) {
818
+ // Intl API failed, continue to fallback
819
+ }
820
+ }
821
+
822
+ if (!systemLocale) {
823
+ return "en"; // Default fallback
824
+ }
825
+
826
+ // Extract language code from locale (e.g., 'zh_CN' -> 'zh', 'en_US' -> 'en')
827
+ const langCode = systemLocale.split(/[-_]/)[0].toLowerCase();
828
+
829
+ // Map to supported language codes
830
+ const supportedLang = SUPPORTED_LANGUAGES.find((lang) => lang.code === langCode);
831
+ if (supportedLang) {
832
+ return supportedLang.code;
833
+ }
834
+
835
+ // Handle special cases for Chinese variants
836
+ if (langCode === "zh") {
837
+ // Check for Traditional Chinese indicators
838
+ const fullLocale = systemLocale.toLowerCase();
839
+ if (fullLocale.includes("tw") || fullLocale.includes("hk") || fullLocale.includes("mo")) {
840
+ return "zh-TW";
841
+ }
842
+ return "zh"; // Default to Simplified Chinese
843
+ }
844
+
845
+ // Return default if no match found
846
+ return "en";
847
+ } catch (_error) {
848
+ // Any error in detection, return default
849
+ return "en";
850
+ }
851
+ }