@aigne/doc-smith 0.4.5 → 0.5.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.
@@ -0,0 +1,175 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { parse, stringify } from "yaml";
5
+
6
+ const PREFERENCES_DIR = ".aigne/doc-smith";
7
+ const PREFERENCES_FILE = "preferences.yml";
8
+
9
+ /**
10
+ * Generate a random preference ID
11
+ * @returns {string} Random ID with pref_ prefix
12
+ */
13
+ function generatePreferenceId() {
14
+ return `pref_${randomBytes(8).toString("hex")}`;
15
+ }
16
+
17
+ /**
18
+ * Get the full path to the preferences file
19
+ * @returns {string} Full path to preferences.yml
20
+ */
21
+ function getPreferencesFilePath() {
22
+ return join(process.cwd(), PREFERENCES_DIR, PREFERENCES_FILE);
23
+ }
24
+
25
+ /**
26
+ * Ensure the preferences directory exists
27
+ */
28
+ function ensurePreferencesDir() {
29
+ const preferencesDir = join(process.cwd(), PREFERENCES_DIR);
30
+ if (!existsSync(preferencesDir)) {
31
+ mkdirSync(preferencesDir, { recursive: true });
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Read existing preferences from file
37
+ * @returns {Object} Preferences object with rules array
38
+ */
39
+ export function readPreferences() {
40
+ const filePath = getPreferencesFilePath();
41
+
42
+ if (!existsSync(filePath)) {
43
+ return { rules: [] };
44
+ }
45
+
46
+ try {
47
+ const content = readFileSync(filePath, "utf8");
48
+ const preferences = parse(content);
49
+ return preferences || { rules: [] };
50
+ } catch (error) {
51
+ console.warn(`Warning: Failed to read preferences file at ${filePath}: ${error.message}`);
52
+ return { rules: [] };
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Write preferences to file
58
+ * @param {Object} preferences - Preferences object to save
59
+ */
60
+ export function writePreferences(preferences) {
61
+ ensurePreferencesDir();
62
+ const filePath = getPreferencesFilePath();
63
+
64
+ try {
65
+ const yamlContent = stringify(preferences, {
66
+ indent: 2,
67
+ lineWidth: 120,
68
+ });
69
+
70
+ writeFileSync(filePath, yamlContent, "utf8");
71
+ } catch (error) {
72
+ throw new Error(`Failed to write preferences file: ${error.message}`);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Add a new preference rule
78
+ * @param {Object} ruleData - Rule data from feedbackRefiner
79
+ * @param {string} ruleData.rule - The rule text
80
+ * @param {string} ruleData.scope - Rule scope (global, structure, document, translation)
81
+ * @param {boolean} ruleData.limitToInputPaths - Whether to limit to input paths
82
+ * @param {string} feedback - Original user feedback
83
+ * @param {string[]} [paths] - Optional paths to save with the rule
84
+ * @returns {Object} The created preference rule
85
+ */
86
+ export function addPreferenceRule(ruleData, feedback, paths = []) {
87
+ const preferences = readPreferences();
88
+
89
+ const newRule = {
90
+ id: generatePreferenceId(),
91
+ active: true,
92
+ scope: ruleData.scope,
93
+ rule: ruleData.rule,
94
+ feedback: feedback,
95
+ createdAt: new Date().toISOString(),
96
+ };
97
+
98
+ // Add paths if limitToInputPaths is true and paths are provided
99
+ if (ruleData.limitToInputPaths && paths && paths.length > 0) {
100
+ newRule.paths = paths;
101
+ }
102
+
103
+ // Add the new rule to the beginning of the array (newest first)
104
+ preferences.rules.unshift(newRule);
105
+
106
+ writePreferences(preferences);
107
+
108
+ return newRule;
109
+ }
110
+
111
+ /**
112
+ * Get all active preference rules for a specific scope
113
+ * @param {string} scope - The scope to filter by (global, structure, document, translation)
114
+ * @param {string[]} [currentPaths] - Current paths to match against rules with path restrictions
115
+ * @returns {Object[]} Array of matching active rules
116
+ */
117
+ export function getActiveRulesForScope(scope, currentPaths = []) {
118
+ const preferences = readPreferences();
119
+
120
+ return preferences.rules.filter((rule) => {
121
+ // Must be active and match scope
122
+ if (!rule.active || rule.scope !== scope) {
123
+ return false;
124
+ }
125
+
126
+ // If rule has path restrictions, check if any current path matches
127
+ if (rule.paths && rule.paths.length > 0) {
128
+ if (currentPaths.length === 0) {
129
+ return false; // Rule has path restrictions but no current paths provided
130
+ }
131
+
132
+ // Check if any current path matches any rule path pattern
133
+ return currentPaths.some((currentPath) => rule.paths.includes(currentPath));
134
+ }
135
+
136
+ return true; // No path restrictions, include the rule
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Deactivate a preference rule by ID
142
+ * @param {string} ruleId - The ID of the rule to deactivate
143
+ * @returns {boolean} True if rule was found and deactivated
144
+ */
145
+ export function deactivateRule(ruleId) {
146
+ const preferences = readPreferences();
147
+ const rule = preferences.rules.find((r) => r.id === ruleId);
148
+
149
+ if (rule) {
150
+ rule.active = false;
151
+ writePreferences(preferences);
152
+ return true;
153
+ }
154
+
155
+ return false;
156
+ }
157
+
158
+ /**
159
+ * Remove a preference rule by ID
160
+ * @param {string} ruleId - The ID of the rule to remove
161
+ * @returns {boolean} True if rule was found and removed
162
+ */
163
+ export function removeRule(ruleId) {
164
+ const preferences = readPreferences();
165
+ const initialLength = preferences.rules.length;
166
+
167
+ preferences.rules = preferences.rules.filter((r) => r.id !== ruleId);
168
+
169
+ if (preferences.rules.length < initialLength) {
170
+ writePreferences(preferences);
171
+ return true;
172
+ }
173
+
174
+ return false;
175
+ }
package/utils/utils.mjs CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  DEFAULT_INCLUDE_PATTERNS,
9
9
  DOCUMENT_STYLES,
10
10
  DOCUMENTATION_DEPTH,
11
- SUPPORTED_FILE_EXTENSIONS,
12
11
  READER_KNOWLEDGE_LEVELS,
12
+ SUPPORTED_FILE_EXTENSIONS,
13
13
  SUPPORTED_LANGUAGES,
14
14
  TARGET_AUDIENCES,
15
15
  } from "./constants.mjs";