@aigne/doc-smith 0.8.5 → 0.8.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.6](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.5...v0.8.6) (2025-09-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * add deploy unit tests and improve publish workflow with better logging ([e33a94b](https://github.com/AIGNE-io/aigne-doc-smith/commit/e33a94bef5eda09398901fa1f953e662ae5fbd16))
9
+ * **publish:** display publish url for the default publish processing ([9d1d018](https://github.com/AIGNE-io/aigne-doc-smith/commit/9d1d0180dc9c8bb0a4393a893eed2395eec300ab))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **deploy:** ensure log is saved after await to prevent save failure ([793343f](https://github.com/AIGNE-io/aigne-doc-smith/commit/793343fc7f96ab962e70eb310cb07f4e7eaec9e0))
15
+
16
+
17
+ ### Miscellaneous Chores
18
+
19
+ * release 0.8.6 ([1e25cb4](https://github.com/AIGNE-io/aigne-doc-smith/commit/1e25cb49a26d8bcc3c83ec36120b6bad4042cadf))
20
+
3
21
  ## [0.8.5](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.4...v0.8.5) (2025-09-10)
4
22
 
5
23
 
@@ -9,7 +9,7 @@ export default async function actionSuccess({ action }) {
9
9
  }
10
10
 
11
11
  return {
12
- message: `✅ ${action} successfully`,
12
+ message: `${action}`,
13
13
  };
14
14
  }
15
15
 
@@ -21,7 +21,7 @@ export default async function checkStructurePlan(
21
21
  // Prompt for feedback if originalStructurePlan exists and no feedback provided
22
22
  if (originalStructurePlan && !feedback) {
23
23
  const userFeedback = await options.prompts.input({
24
- message: "Please provide feedback for structure planning (press Enter to skip):",
24
+ message: "How can we improve the documentation structure? (press Enter to skip):",
25
25
  });
26
26
 
27
27
  if (userFeedback?.trim()) {
@@ -2,7 +2,7 @@ type: team
2
2
  name: update
3
3
  alias:
4
4
  - up
5
- description: Optimize and regenerate individual document content and translations
5
+ description: Update specific documents and their translations
6
6
  skills:
7
7
  - url: ./input-generator.mjs
8
8
  default_input:
@@ -39,24 +39,24 @@ skills:
39
39
  stage: document_refine
40
40
  - url: ./action-success.mjs
41
41
  default_input:
42
- action: "Document updated"
42
+ action: " Documents updated successfully"
43
43
  input_schema:
44
44
  type: object
45
45
  properties:
46
46
  glossary:
47
47
  type: string
48
- description: Glossary of terms for consistent terminology, use @<file> to read from a file
48
+ description: Glossary file for consistent terminology (use @filename.md)
49
49
  docs:
50
50
  type: array
51
51
  items:
52
52
  type: string
53
- description: Document paths to translate
53
+ description: Documents to update
54
54
  feedback:
55
55
  type: string
56
- description: Feedback for content improvement
56
+ description: Tell us what to change in this content
57
57
  reset:
58
58
  type: boolean
59
- description: Ignore previous results and regenerate content from scratch
59
+ description: Start fresh - ignore previous versions
60
60
  output_schema:
61
61
  type: object
62
62
  properties:
@@ -3,7 +3,7 @@ name: generate
3
3
  alias:
4
4
  - gen
5
5
  - g
6
- description: Automatically generates comprehensive project documentation
6
+ description: Generate complete documentation for your project
7
7
  skills:
8
8
  - url: ./input-generator.mjs
9
9
  default_input:
@@ -47,13 +47,13 @@ input_schema:
47
47
  properties:
48
48
  glossary:
49
49
  type: string
50
- description: Glossary of terms for consistent terminology, use @<file> to read from a file
50
+ description: Glossary file for consistent terminology (use @filename.md)
51
51
  feedback:
52
52
  type: string
53
- description: Feedback for structure planning adjustments
53
+ description: Tell us how to improve the documentation structure
54
54
  forceRegenerate:
55
55
  type: boolean
56
- description: Force regenerate all documentation
56
+ description: Rebuild all documentation from scratch
57
57
  required:
58
58
  - config
59
59
  mode: sequential
@@ -1,5 +1,5 @@
1
1
  name: feedbackRefiner
2
- description: Analyzes a user's specific feedback on generated documentation and refines it into a general, reusable preference rule for future use.
2
+ description: Learn from your feedback to improve future documentation
3
3
 
4
4
  # The instructions file contains the core logic (the prompt) for this Agent.
5
5
  instructions:
@@ -10,18 +10,18 @@ input_schema:
10
10
  properties:
11
11
  feedback:
12
12
  type: string
13
- description: User's original feedback
13
+ description: Tell us what you think about the documentation
14
14
  stage:
15
15
  type: string
16
- description: The command/scenario that generated the feedback (used to infer scope), possible values. structure_planning, document_refine, translation_refine
16
+ description: Which part of the process this feedback is about (structure_planning, document_refine, translation_refine)
17
17
  paths:
18
18
  type: array
19
19
  items:
20
20
  type: string
21
- description: Optional. Document paths specified by the user in the current command
21
+ description: Specific documents this feedback applies to (optional)
22
22
  existingPreferences:
23
23
  type: string
24
- description: Optional. YAML format string of currently saved user preference rules, used to avoid duplicate saving of similar rules
24
+ description: Your existing preferences to avoid duplicates (optional)
25
25
  required:
26
26
  - feedback
27
27
  - stage
@@ -31,19 +31,19 @@ output_schema:
31
31
  properties:
32
32
  rule:
33
33
  type: string
34
- description: Refine and summarize user feedback into a general rule
34
+ description: General rule created from your feedback
35
35
  scope:
36
36
  type: string
37
- description: Rule scope. global, structure, document, translation
37
+ description: Where this rule applies (global, structure, document, translation)
38
38
  save:
39
39
  type: boolean
40
- description: Whether to save as persistent preference (true=save; false=one-time, do not save)
40
+ description: Should we remember this preference for next time?
41
41
  limitToInputPaths:
42
42
  type: boolean
43
- description: Whether to limit to the "paths specified in current input" when used subsequently
43
+ description: Apply only to the specific documents mentioned?
44
44
  reason:
45
45
  type: string
46
- description: Explanation of why the save decision was made and how the rule and scope were derived
46
+ description: Why we made this decision and how the rule was created
47
47
  required:
48
48
  - rule
49
49
  - scope
@@ -89,7 +89,7 @@ export default async function findItemByPath(
89
89
  if (!userFeedback) {
90
90
  const feedbackMessage = getActionText(
91
91
  isTranslate,
92
- "Please provide feedback for the {action} (press Enter to skip):",
92
+ "How should we improve this {action}? (press Enter to skip):",
93
93
  );
94
94
 
95
95
  userFeedback = await options.prompts.input({
@@ -90,7 +90,7 @@ export default async function selectedDocs(
90
90
  if (!userFeedback) {
91
91
  const feedbackMessage = getActionText(
92
92
  isTranslate,
93
- "Please provide feedback for the {action} (press Enter to skip):",
93
+ "How should we improve this {action}? (press Enter to skip):",
94
94
  );
95
95
 
96
96
  userFeedback = await options.prompts.input({
@@ -44,14 +44,14 @@ export default async function init(
44
44
  }
45
45
 
46
46
  console.log("🚀 Welcome to AIGNE DocSmith!");
47
- console.log("Let's create your documentation configuration.\n");
47
+ console.log("Let's set up your documentation preferences.\n");
48
48
 
49
49
  // Collect user information
50
50
  const input = {};
51
51
 
52
52
  // 1. Primary purpose - what's the main outcome you want readers to achieve?
53
53
  const purposeChoices = await options.prompts.checkbox({
54
- message: "📝 [1/8]: What is the primary goal for your readers? (Select all that apply)",
54
+ message: "📝 [1/8]: What should your documentation help readers achieve?",
55
55
  choices: Object.entries(DOCUMENT_STYLES)
56
56
  .filter(([key]) => key !== "custom") // Remove custom option for multiselect
57
57
  .map(([key, style]) => ({
@@ -61,7 +61,7 @@ export default async function init(
61
61
  })),
62
62
  validate: (input) => {
63
63
  if (input.length === 0) {
64
- return "Please select at least one purpose.";
64
+ return "Please choose at least one goal for your documentation.";
65
65
  }
66
66
  return true;
67
67
  },
@@ -81,10 +81,10 @@ export default async function init(
81
81
  })),
82
82
  validate: (input) => {
83
83
  if (input.length === 0) {
84
- return "Please select at least one priority.";
84
+ return "Please choose at least one priority.";
85
85
  }
86
86
  if (input.length > 2) {
87
- return "Please select maximum 2 priorities.";
87
+ return "Please choose maximum 2 priorities.";
88
88
  }
89
89
  return true;
90
90
  },
@@ -99,7 +99,7 @@ export default async function init(
99
99
 
100
100
  // 2. Target audience - who will be reading this most often?
101
101
  const audienceChoices = await options.prompts.checkbox({
102
- message: "👥 [2/8]: Who is the primary audience for this documentation?",
102
+ message: "👥 [2/8]: Who will be reading your documentation?",
103
103
  choices: Object.entries(TARGET_AUDIENCES)
104
104
  .filter(([key]) => key !== "custom") // Remove custom option for multiselect
105
105
  .map(([key, audience]) => ({
@@ -109,7 +109,7 @@ export default async function init(
109
109
  })),
110
110
  validate: (input) => {
111
111
  if (input.length === 0) {
112
- return "Please select at least one audience.";
112
+ return "Please choose at least one audience.";
113
113
  }
114
114
  return true;
115
115
  },
@@ -133,7 +133,7 @@ export default async function init(
133
133
  );
134
134
 
135
135
  const knowledgeChoice = await options.prompts.select({
136
- message: "🧠 [3/8]: What is your reader's typical starting knowledge level?",
136
+ message: "🧠 [3/8]: How much do readers already know about your project?",
137
137
  choices: Object.entries(filteredKnowledgeOptions).map(([key, level]) => ({
138
138
  name: `${level.name}`,
139
139
  description: level.description,
@@ -178,7 +178,7 @@ export default async function init(
178
178
  );
179
179
 
180
180
  const depthChoice = await options.prompts.select({
181
- message: "📊 [4/8]: How comprehensive should the documentation be?",
181
+ message: "📊 [4/8]: How detailed should your documentation be?",
182
182
  choices: Object.entries(filteredDepthOptions).map(([key, depth]) => ({
183
183
  name: `${depth.name}`,
184
184
  description: depth.description,
@@ -196,7 +196,7 @@ export default async function init(
196
196
 
197
197
  // Let user select primary language from supported list
198
198
  const primaryLanguageChoice = await options.prompts.select({
199
- message: "🌐 [5/8]: Choose primary documentation language:",
199
+ message: "🌐 [5/8]: What's your main documentation language?",
200
200
  choices: SUPPORTED_LANGUAGES.map((lang) => ({
201
201
  name: `${lang.label} - ${lang.sample}`,
202
202
  value: lang.code,
@@ -213,7 +213,7 @@ export default async function init(
213
213
  );
214
214
 
215
215
  const translateLanguageChoices = await options.prompts.checkbox({
216
- message: "🔄 [6/8]: Select translation languages:",
216
+ message: "🔄 [6/8]: Which languages should we translate to?",
217
217
  choices: availableTranslationLanguages.map((lang) => ({
218
218
  name: `${lang.label} - ${lang.sample}`,
219
219
  value: lang.code,
@@ -224,21 +224,23 @@ export default async function init(
224
224
 
225
225
  // 7. Documentation directory
226
226
  const docsDirInput = await options.prompts.input({
227
- message: `📁 [7/8]: Where to save generated docs:`,
227
+ message: `📁 [7/8]: Where should we save your documentation?`,
228
228
  default: `${outputPath}/docs`,
229
229
  });
230
230
  input.docsDir = docsDirInput.trim() || `${outputPath}/docs`;
231
231
 
232
- // 8. Source code paths
233
- console.log("\n🔍 [8/8]: Source Code Paths");
234
- console.log("Enter paths to analyze for documentation (e.g., ./src, ./lib)");
235
- console.log("💡 You can also enter glob patterns (e.g., src/**/*.js, **/*.md)");
236
- console.log("💡 If no paths are configured, './' will be used as default");
232
+ // 8. Content sources
233
+ console.log("\n🔍 [8/8]: Content Sources");
234
+ console.log(
235
+ "What folders/files should we analyze for documentation? (e.g., ./src, ./docs, ./README.md)",
236
+ );
237
+ console.log("💡 Advanced: Use patterns like src/**/*.js or docs/**/*.md for specific files");
238
+ console.log("💡 Leave empty to scan everything");
237
239
 
238
240
  const sourcePaths = [];
239
241
  while (true) {
240
242
  const selectedPath = await options.prompts.search({
241
- message: "Path or glob pattern:",
243
+ message: "File/folder path or pattern:",
242
244
  source: async (input) => {
243
245
  if (!input || input.trim() === "") {
244
246
  return [
@@ -330,15 +332,13 @@ export default async function init(
330
332
  await mkdir(dirPath, { recursive: true });
331
333
 
332
334
  await writeFile(filePath, yamlContent, "utf8");
333
- console.log(`\n🎉 Configuration saved to: ${chalk.cyan(filePath)}`);
335
+ console.log(`\n Setup complete! Configuration saved to: ${chalk.cyan(filePath)}`);
334
336
  // Print YAML content for user review
335
337
  console.log(chalk.cyan("---"));
336
338
  console.log(chalk.cyan(yamlContent));
337
339
  console.log(chalk.cyan("---"));
338
340
  console.log("💡 You can edit the configuration file anytime to modify settings.\n");
339
- console.log(
340
- `🚀 Run ${chalk.cyan("'aigne doc generate'")} to start documentation generation!\n`,
341
- );
341
+ console.log(`🚀 Ready to generate docs? Run: ${chalk.cyan("aigne doc generate")}\n`);
342
342
 
343
343
  return {};
344
344
  } catch (error) {
@@ -8,7 +8,7 @@ function listPreferences() {
8
8
  const preferences = readPreferences();
9
9
 
10
10
  if (preferences.rules.length === 0) {
11
- return { message: "No preferences found." };
11
+ return { message: "No saved preferences found." };
12
12
  }
13
13
 
14
14
  let message = "# User Preferences\n\n";
@@ -53,7 +53,7 @@ async function removePreferences(id, options) {
53
53
  if (!targetIds || targetIds.length === 0) {
54
54
  // Interactive selection
55
55
  if (preferences.rules.length === 0) {
56
- return { message: "No preferences found to remove." };
56
+ return { message: "No preferences available to remove." };
57
57
  }
58
58
 
59
59
  const choices = preferences.rules.map((rule) => ({
@@ -63,18 +63,18 @@ async function removePreferences(id, options) {
63
63
  }));
64
64
 
65
65
  targetIds = await options.prompts.checkbox({
66
- message: "Select preferences to remove:",
66
+ message: "Choose preferences to delete:",
67
67
  choices,
68
68
  validate: (answer) => {
69
69
  if (answer.length === 0) {
70
- return "Please select at least one preference to remove";
70
+ return "Please choose at least one preference to delete";
71
71
  }
72
72
  return true;
73
73
  },
74
74
  });
75
75
 
76
76
  if (!targetIds || targetIds.length === 0) {
77
- return { message: "No preferences selected for removal." };
77
+ return { message: "No preferences selected for deletion." };
78
78
  }
79
79
  }
80
80
 
@@ -108,7 +108,7 @@ async function togglePreferences(id, options) {
108
108
  if (!targetIds || targetIds.length === 0) {
109
109
  // Interactive selection
110
110
  if (preferences.rules.length === 0) {
111
- return { message: "No preferences found to toggle." };
111
+ return { message: "No preferences available to toggle." };
112
112
  }
113
113
 
114
114
  const choices = preferences.rules.map((rule) => ({
@@ -118,18 +118,18 @@ async function togglePreferences(id, options) {
118
118
  }));
119
119
 
120
120
  targetIds = await options.prompts.checkbox({
121
- message: "Select preferences to toggle active status:",
121
+ message: "Choose preferences to enable/disable:",
122
122
  choices,
123
123
  validate: (answer) => {
124
124
  if (answer.length === 0) {
125
- return "Please select at least one preference to toggle";
125
+ return "Please choose at least one preference to toggle";
126
126
  }
127
127
  return true;
128
128
  },
129
129
  });
130
130
 
131
131
  if (!targetIds || targetIds.length === 0) {
132
- return { message: "No preferences selected for toggling." };
132
+ return { message: "No preferences selected to toggle." };
133
133
  }
134
134
  }
135
135
 
@@ -172,7 +172,7 @@ export default async function prefs({ list, remove, toggle, id }, options) {
172
172
  return await togglePreferences(id, options);
173
173
  }
174
174
 
175
- return { message: "Please specify an action: --list, --remove, or --toggle." };
175
+ return { message: "Please choose an action: --list, --remove, or --toggle." };
176
176
  }
177
177
 
178
178
  prefs.input_schema = {
@@ -180,24 +180,24 @@ prefs.input_schema = {
180
180
  properties: {
181
181
  list: {
182
182
  type: "boolean",
183
- description: "List all preferences",
183
+ description: "Show all saved preferences",
184
184
  },
185
185
  remove: {
186
186
  type: "boolean",
187
- description: "Remove preferences",
187
+ description: "Delete saved preferences",
188
188
  },
189
189
  toggle: {
190
190
  type: "boolean",
191
- description: "Toggle preferences active status",
191
+ description: "Enable/disable preferences",
192
192
  },
193
193
  id: {
194
194
  type: "array",
195
195
  items: {
196
196
  type: "string",
197
197
  },
198
- description: "Preference IDs to manage",
198
+ description: "Specific preference IDs to work with",
199
199
  },
200
200
  },
201
201
  };
202
202
 
203
- prefs.description = "Manage user preferences learned from feedback";
203
+ prefs.description = "Manage your saved documentation preferences";
@@ -53,21 +53,17 @@ export default async function publishDocs(
53
53
  message: "Select platform to publish your documents:",
54
54
  choices: [
55
55
  {
56
- name:
57
- chalk.blue("Publish to docsmith.aigne.io") +
58
- " - free, but your documents will be publicly accessible, recommended for open-source projects",
56
+ name: `${chalk.blue("DocSmith Cloud (docsmith.aigne.io)")} – ${chalk.green("Free")} hosting. Your documents will be publicly accessible. Best for open-source projects or community sharing.`,
59
57
  value: "default",
60
58
  },
61
59
  {
62
- name: `${chalk.blue("Publish to your existing website")} - use your current website`,
60
+ name: `${chalk.blue("Your existing website")} - Integrate and publish directly on your current site (setup required)`,
63
61
  value: "custom",
64
62
  },
65
63
  ...(hasCachedCheckoutId && hasDocSmithBaseUrl
66
64
  ? [
67
65
  {
68
- name:
69
- chalk.yellow("Continue your previous website setup") +
70
- " - resume from where you left off",
66
+ name: `${chalk.yellow("Resume previous website setup")} - ${chalk.green("Already paid.")} Continue where you left off. Your payment is already processed.`,
71
67
  value: "new-instance-continue",
72
68
  },
73
69
  ]
@@ -75,7 +71,7 @@ export default async function publishDocs(
75
71
  ...(hasDocSmithBaseUrl
76
72
  ? [
77
73
  {
78
- name: `${chalk.blue("Publish to a new website")} - we'll help you set up a new website`,
74
+ name: `${chalk.blue("New website")} - ${chalk.yellow("Paid service.")} We'll help you set up a brand-new website with custom domain and hosting. Great if you want a professional presence.`,
79
75
  value: "new-instance",
80
76
  },
81
77
  ]
@@ -113,7 +109,7 @@ export default async function publishDocs(
113
109
  paymentUrl = config?.paymentUrl;
114
110
  console.log(`\nResuming your previous website setup...`);
115
111
  } else {
116
- console.log(`\nCreating a new doc website for your documentation...`);
112
+ console.log(`\nCreating new website for your documentation...`);
117
113
  }
118
114
  const { appUrl: homeUrl, token: ltToken } = (await deploy(id, paymentUrl)) || {};
119
115
 
@@ -127,6 +123,8 @@ export default async function publishDocs(
127
123
  }
128
124
  }
129
125
 
126
+ console.log(`\nPublishing docs to ${chalk.cyan(appUrl)}\n`);
127
+
130
128
  const accessToken = await getAccessToken(appUrl, token);
131
129
 
132
130
  process.env.DOC_ROOT_DIR = docsDir;
@@ -185,7 +183,7 @@ export default async function publishDocs(
185
183
  } catch (error) {
186
184
  message = `❌ Failed to publish docs: ${error.message}`;
187
185
  }
188
- saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
186
+ await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
189
187
 
190
188
  // clean up tmp work dir
191
189
  await fs.rm(docsDir, { recursive: true, force: true });
@@ -211,4 +209,4 @@ publishDocs.input_schema = {
211
209
  },
212
210
  };
213
211
 
214
- publishDocs.description = "Publish the documentation to Discuss Kit";
212
+ publishDocs.description = "Publish the documentation to website";
@@ -1,6 +1,6 @@
1
1
  type: team
2
2
  name: translate
3
- description: Translate document content to selected languages
3
+ description: Translate documents into other languages
4
4
  skills:
5
5
  - url: ./input-generator.mjs
6
6
  default_input:
@@ -42,24 +42,24 @@ skills:
42
42
  stage: translation_refine
43
43
  - url: ./action-success.mjs
44
44
  default_input:
45
- action: "Document translated"
45
+ action: " Translation completed"
46
46
  input_schema:
47
47
  type: object
48
48
  properties:
49
49
  glossary:
50
50
  type: string
51
- description: Glossary of terms for consistent terminology, use @<file> to read from a file
51
+ description: Glossary file for consistent terminology (use @filename.md)
52
52
  docs:
53
53
  type: array
54
54
  items:
55
55
  type: string
56
- description: Document paths to translate
56
+ description: Documents to translate
57
57
  langs:
58
58
  type: array
59
59
  items:
60
60
  type: string
61
- description: "Languages to translate to, available languages are: en, zh, zh-TW, ja, fr, de, es, it, ru, ko, pt, ar"
61
+ description: "Target languages (available: en, zh, zh-TW, ja, fr, de, es, it, ru, ko, pt, ar)"
62
62
  feedback:
63
63
  type: string
64
- description: Feedback for translation improvement
64
+ description: Tell us how to improve the translation style
65
65
  mode: sequential
@@ -1,5 +1,5 @@
1
1
  name: structurePlanGenerator
2
- description: 通用结构规划生成器,支持网站、文档、书籍、演示文稿等多种场景
2
+ description: Plan the structure and organization of your documentation
3
3
  instructions:
4
4
  url: ../prompts/structure-planning.md
5
5
  input_schema:
@@ -7,31 +7,31 @@ input_schema:
7
7
  properties:
8
8
  rules:
9
9
  type: string
10
- description: 用户的结构规划的要求
10
+ description: Your specific requirements for documentation structure
11
11
  locale:
12
12
  type: string
13
- description: 用户语言,如 zhen
13
+ description: Primary language for documentation (e.g., zh, en, ja)
14
14
  datasources:
15
15
  type: string
16
- description: 结构规划的上下文,用于辅助结构规划
16
+ description: Project content and context to help plan structure
17
17
  targetAudience:
18
18
  type: string
19
- description: 结构规划的目标受众
19
+ description: Target audience for the documentation
20
20
  nodeName:
21
21
  type: string
22
- description: 结构规划的节点名称
22
+ description: Specific section or page name to focus on
23
23
  glossary:
24
24
  type: string
25
- description: 术语表
25
+ description: Glossary for consistent terminology
26
26
  feedback:
27
27
  type: string
28
- description: 结构规划的反馈
28
+ description: Tell us how to improve the documentation structure
29
29
  userPreferences:
30
30
  type: string
31
- description: 用户偏好规则,YAML格式的结构规划和全局范围偏好
31
+ description: Your saved preferences for structure and documentation style
32
32
  docsType:
33
33
  type: string
34
- description: 文档类型,支持:generalgetting-startedreferencefaq
34
+ description: "Documentation type (options: general, getting-started, reference, faq)"
35
35
  default: general
36
36
  required:
37
37
  - rules
@@ -41,18 +41,18 @@ output_schema:
41
41
  properties:
42
42
  projectName:
43
43
  type: string
44
- description: 根据 DataSources 分析项目名称,注意是原始项目名称
44
+ description: Project name identified from your content sources
45
45
  projectDesc:
46
46
  type: string
47
- description: 根据 DataSources 分析生成当前项目描述,为原始项目生成描述,描述简洁,不超过 50 个单词
47
+ description: Brief project description generated from content analysis (under 50 words)
48
48
  structurePlan: ./schema/structure-plan.yaml
49
49
  structurePlanTree:
50
50
  type: string
51
51
  description: |
52
- 结构规划的树形结构,用于展示结构规划的层级关系,每一级有不同的缩进,方便用户直观查看结构规划的层级关系,参考格式:
52
+ Visual tree structure showing documentation hierarchy with indented levels for easy review:
53
53
  ```
54
- - 首页
55
- - 产品
56
- - 产品详情
57
- - 产品介绍
54
+ - Home
55
+ - Getting Started
56
+ - Installation
57
+ - Requirements
58
58
  ```
@@ -3,7 +3,7 @@ name: publish
3
3
  alias:
4
4
  - pub
5
5
  - p
6
- description: Publish the documentation to Discuss Kit
6
+ description: Publish your documentation online
7
7
  skills:
8
8
  - url: ./input-generator.mjs
9
9
  default_input:
@@ -15,4 +15,4 @@ input_schema:
15
15
  properties:
16
16
  appUrl:
17
17
  type: string
18
- description: target website URL where the documentation will be published (optional - if not provided, will prompt for interactive input)
18
+ description: Website URL where docs will be published (optional - leave empty for interactive setup)
@@ -1,5 +1,5 @@
1
1
  name: translateDoc
2
- description: Translate the wiki article to another language
2
+ description: Translate content to another language
3
3
  instructions:
4
4
  url: ../prompts/translator.md
5
5
  input_schema:
@@ -7,16 +7,16 @@ input_schema:
7
7
  properties:
8
8
  language:
9
9
  type: string
10
- description: Language to translate the article into (e.g., 'zh_CN' for Chinese)
10
+ description: Target language (e.g., 'zh' for Chinese, 'ja' for Japanese)
11
11
  content:
12
12
  type: string
13
- description: Content to translate
13
+ description: Text content to translate
14
14
  glossary:
15
15
  type: string
16
- description: 术语表
16
+ description: Glossary for consistent terminology
17
17
  feedback:
18
18
  type: string
19
- description: Feedback for translation improvement
19
+ description: Tell us how to improve the translation style
20
20
  required:
21
21
  - language
22
22
  - content
@@ -25,7 +25,7 @@ output_schema:
25
25
  properties:
26
26
  translation:
27
27
  type: string
28
- description: Translation of the content
28
+ description: Translated text
29
29
  language:
30
30
  type: string
31
- description: Language of the translation
31
+ description: Language code of the translation
@@ -89,7 +89,7 @@ Analyzes your source code and generates a complete set of documentation based on
89
89
 
90
90
  | Option | Type | Description |
91
91
  |---|---|---|
92
- | `--feedback` | string | Provides feedback to adjust and refine the overall document structure plan. |
92
+ | `--feedback` | string | Provides feedback to adjust and refine the overall documentation structure. |
93
93
  | `--forceRegenerate` | boolean | Discards existing content and regenerates all documentation from scratch. |
94
94
  | `--model` | string | Specifies a particular LLM to use for generation (e.g., `openai:gpt-4o`). Overrides the default model. |
95
95
  | `--glossary` | string | Path to a glossary file for consistent terminology. Use the format `@path/to/glossary.md`. |
@@ -90,7 +90,7 @@ While the default `generate` command is sufficient for most use cases, you can u
90
90
  | Option | Description | Example |
91
91
  |---------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
92
92
  | `--forceRegenerate` | Deletes all existing documents and regenerates them from scratch. Use this after making significant changes to your source code or configuration. | `aigne doc generate --forceRegenerate` |
93
- | `--feedback` | Provides high-level feedback to refine the overall document structure plan, such as adding, removing, or reorganizing sections. | `aigne doc generate --feedback "Add an API Reference section"` |
93
+ | `--feedback` | Provides high-level feedback to refine the overall documentation structure, such as adding, removing, or reorganizing sections. | `aigne doc generate --feedback "Add an API Reference section"` |
94
94
  | `--model` | Specifies a particular Large Language Model from AIGNE Hub to use for content generation, allowing you to switch between models. | `aigne doc generate --model claude:claude-3-5-sonnet` |
95
95
 
96
96
  ## What's Next?
@@ -123,12 +123,12 @@ Key parameters for the `update` command:
123
123
 
124
124
  ## Optimizing the Overall Structure
125
125
 
126
- Beyond refining the content of individual documents, you can also adjust the overall documentation structure. If a section is missing or the existing organization could be improved, you can provide feedback to the structure planning agent using the `generate` command with the `--feedback` flag.
126
+ Beyond refining the content of individual documents, you can also adjust the overall documentation structure. If a section is missing or the existing organization could be improved, you can provide feedback to improve the documentation structure using the `generate` command with the `--feedback` flag.
127
127
 
128
128
  This command instructs DocSmith to reconsider the entire document plan based on your new input.
129
129
 
130
130
  ```bash
131
- # Regenerate the structure plan with specific feedback
131
+ # Regenerate the documentation structure with specific feedback
132
132
  aigne doc generate --feedback "Remove the 'About' section and add a more detailed 'API Reference'."
133
133
  ```
134
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,376 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
2
+
3
+ // Create mock functions for blocklet module only
4
+ const mockGetComponentInfoWithMountPoint = mock();
5
+ const mockGetComponentInfo = mock();
6
+
7
+ // Mock only the blocklet module globally (it's safe as it's specific to this functionality)
8
+ mock.module("../utils/blocklet.mjs", () => ({
9
+ getComponentInfoWithMountPoint: mockGetComponentInfoWithMountPoint,
10
+ getComponentInfo: mockGetComponentInfo,
11
+ }));
12
+
13
+ // Mock the open module to prevent opening browser during tests
14
+ const mockOpenDefault = mock(() => Promise.resolve());
15
+ mock.module("open", () => ({
16
+ default: mockOpenDefault,
17
+ }));
18
+
19
+ // Import the real utils module and deploy function
20
+ import { deploy } from "../utils/deploy.mjs";
21
+ import * as utils from "../utils/utils.mjs";
22
+
23
+ describe("deploy function", () => {
24
+ let originalFetch;
25
+ let originalConsole;
26
+ let consoleOutput;
27
+ let saveValueToConfigSpy;
28
+ let originalSetTimeout;
29
+
30
+ beforeEach(async () => {
31
+ // Reset environment
32
+ process.env.DOC_SMITH_BASE_URL = "https://test.example.com";
33
+ process.env.NODE_ENV = "test";
34
+
35
+ // Mock console to capture output
36
+ consoleOutput = [];
37
+ originalConsole = {
38
+ log: console.log,
39
+ error: console.error,
40
+ };
41
+ console.log = (...args) => consoleOutput.push({ type: "log", args });
42
+ console.error = (...args) => consoleOutput.push({ type: "error", args });
43
+
44
+ // Mock setTimeout to make tests run instantly
45
+ originalSetTimeout = global.setTimeout;
46
+ global.setTimeout = (callback, _delay) => {
47
+ // Call immediately in tests
48
+ return originalSetTimeout(callback, 0);
49
+ };
50
+
51
+ // Mock fetch
52
+ originalFetch = global.fetch;
53
+
54
+ // Use spyOn to mock saveValueToConfig without affecting other tests
55
+ saveValueToConfigSpy = spyOn(utils, "saveValueToConfig").mockResolvedValue();
56
+
57
+ // Reset blocklet mocks
58
+ mockGetComponentInfoWithMountPoint.mockReset();
59
+ mockGetComponentInfo.mockReset();
60
+
61
+ // Set default mock implementations
62
+ mockGetComponentInfoWithMountPoint.mockResolvedValue({
63
+ mountPoint: "/payment",
64
+ PAYMENT_LINK_ID: "test-payment-id",
65
+ });
66
+
67
+ mockGetComponentInfo.mockResolvedValue({
68
+ status: "running",
69
+ });
70
+ });
71
+
72
+ afterEach(() => {
73
+ // Restore originals
74
+ global.fetch = originalFetch;
75
+ global.setTimeout = originalSetTimeout;
76
+ console.log = originalConsole.log;
77
+ console.error = originalConsole.error;
78
+
79
+ // Restore spies
80
+ if (saveValueToConfigSpy) {
81
+ saveValueToConfigSpy.mockRestore();
82
+ }
83
+
84
+ // Reset open mock
85
+ mockOpenDefault.mockReset();
86
+
87
+ // Reset environment
88
+ delete process.env.NODE_ENV;
89
+ });
90
+
91
+ test("successful deployment flow", async () => {
92
+ // Mock API responses for the complete flow
93
+ let callCount = 0;
94
+ global.fetch = mock(async (url) => {
95
+ callCount++;
96
+
97
+ // Step 1: Create payment session
98
+ if (url.includes("/api/checkout-sessions/start")) {
99
+ return {
100
+ ok: true,
101
+ status: 200,
102
+ json: async () => ({
103
+ checkoutSession: { id: "checkout-123" },
104
+ paymentUrl: "https://payment.test/checkout-123",
105
+ }),
106
+ };
107
+ }
108
+
109
+ // Step 2-4: Poll payment/installation/service status
110
+ if (url.includes("/api/vendors/order/checkout-123/status")) {
111
+ if (callCount <= 2) {
112
+ // First call: payment completed, installation in progress
113
+ return {
114
+ ok: true,
115
+ status: 200,
116
+ json: async () => ({
117
+ payment_status: "paid",
118
+ vendors: [{ id: "vendor-1", progress: 50, appUrl: null }],
119
+ }),
120
+ };
121
+ } else {
122
+ // Subsequent calls: installation complete
123
+ return {
124
+ ok: true,
125
+ status: 200,
126
+ json: async () => ({
127
+ payment_status: "paid",
128
+ vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
129
+ }),
130
+ };
131
+ }
132
+ }
133
+
134
+ // Step 5: Get order details
135
+ if (url.includes("/api/vendors/order/checkout-123/detail")) {
136
+ return {
137
+ ok: true,
138
+ status: 200,
139
+ json: async () => ({
140
+ vendors: [
141
+ {
142
+ appUrl: "https://app.test",
143
+ dashboardUrl: "https://dashboard.test",
144
+ homeUrl: "https://home.test",
145
+ token: "auth-token-123",
146
+ },
147
+ ],
148
+ }),
149
+ };
150
+ }
151
+
152
+ throw new Error(`Unexpected URL: ${url}`);
153
+ });
154
+
155
+ const result = await deploy();
156
+
157
+ // Verify result
158
+ expect(result).toEqual({
159
+ appUrl: "https://app.test",
160
+ homeUrl: "https://home.test",
161
+ token: "auth-token-123",
162
+ });
163
+
164
+ // Verify saveValueToConfig was called
165
+ expect(saveValueToConfigSpy).toHaveBeenCalledWith(
166
+ "checkoutId",
167
+ "checkout-123",
168
+ "Checkout ID for document deployment service",
169
+ );
170
+ expect(saveValueToConfigSpy).toHaveBeenCalledWith(
171
+ "paymentUrl",
172
+ expect.stringContaining("payment"),
173
+ "Payment URL for document deployment service",
174
+ );
175
+
176
+ // Verify console output shows progress
177
+ const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
178
+ expect(logs.some((log) => log.includes("Step 1/4: Waiting for payment"))).toBe(true);
179
+ expect(logs.some((log) => log.includes("Step 2/4: Installing service"))).toBe(true);
180
+ expect(logs.some((log) => log.includes("Step 3/4: Starting service"))).toBe(true);
181
+ expect(logs.some((log) => log.includes("Step 4/4: Getting service URL"))).toBe(true);
182
+ expect(logs.some((log) => log.includes("Your website is available at"))).toBe(true);
183
+ });
184
+
185
+ test("handles missing payment link ID", async () => {
186
+ mockGetComponentInfoWithMountPoint.mockResolvedValue({
187
+ mountPoint: "/payment",
188
+ PAYMENT_LINK_ID: null,
189
+ });
190
+
191
+ await expect(deploy()).rejects.toThrow("Payment link ID not found");
192
+ });
193
+
194
+ test("handles payment session creation failure", async () => {
195
+ global.fetch = mock(async (url) => {
196
+ if (url.includes("/api/checkout-sessions/start")) {
197
+ return {
198
+ ok: false,
199
+ status: 400,
200
+ json: async () => ({ error: "Payment creation failed" }),
201
+ };
202
+ }
203
+ });
204
+
205
+ await expect(deploy()).rejects.toThrow("Failed to create payment session");
206
+
207
+ const errors = consoleOutput.filter((o) => o.type === "error");
208
+ expect(errors.length).toBeGreaterThan(0);
209
+ });
210
+
211
+ test("handles network errors gracefully", async () => {
212
+ global.fetch = mock(async () => {
213
+ throw new Error("Network connection failed");
214
+ });
215
+
216
+ await expect(deploy()).rejects.toThrow("Failed to create payment session");
217
+ });
218
+
219
+ test("handles browser opening failure gracefully", async () => {
220
+ // Mock successful API flow
221
+ global.fetch = mock(async (url) => {
222
+ if (url.includes("/api/checkout-sessions/start")) {
223
+ return {
224
+ ok: true,
225
+ status: 200,
226
+ json: async () => ({
227
+ checkoutSession: { id: "checkout-123" },
228
+ paymentUrl: "https://payment.test/checkout-123",
229
+ }),
230
+ };
231
+ }
232
+
233
+ if (url.includes("/status")) {
234
+ return {
235
+ ok: true,
236
+ status: 200,
237
+ json: async () => ({
238
+ payment_status: "paid",
239
+ vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
240
+ }),
241
+ };
242
+ }
243
+
244
+ if (url.includes("/detail")) {
245
+ return {
246
+ ok: true,
247
+ status: 200,
248
+ json: async () => ({
249
+ vendors: [
250
+ {
251
+ appUrl: "https://app.test",
252
+ homeUrl: "https://home.test",
253
+ token: "auth-token-123",
254
+ },
255
+ ],
256
+ }),
257
+ };
258
+ }
259
+ });
260
+
261
+ // Mock open to fail
262
+ mockOpenDefault.mockRejectedValue(new Error("Cannot open browser"));
263
+
264
+ // Call deploy without cached parameters - should still succeed
265
+ const result = await deploy();
266
+
267
+ // Should still complete successfully despite browser opening failure
268
+ expect(result.appUrl).toBe("https://app.test");
269
+ expect(result.homeUrl).toBe("https://home.test");
270
+ expect(result.token).toBe("auth-token-123");
271
+ });
272
+
273
+ test("handles cached checkout ID", async () => {
274
+ // Mock successful status check for cached ID
275
+ global.fetch = mock(async (url) => {
276
+ if (url.includes("/status")) {
277
+ return {
278
+ ok: true,
279
+ status: 200,
280
+ json: async () => ({
281
+ payment_status: "paid",
282
+ vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
283
+ }),
284
+ };
285
+ }
286
+
287
+ if (url.includes("/detail")) {
288
+ return {
289
+ ok: true,
290
+ status: 200,
291
+ json: async () => ({
292
+ vendors: [
293
+ {
294
+ appUrl: "https://app.test",
295
+ homeUrl: "https://home.test",
296
+ token: "auth-token-123",
297
+ },
298
+ ],
299
+ }),
300
+ };
301
+ }
302
+ });
303
+
304
+ const result = await deploy("existing-checkout-id", "https://cached-payment.url");
305
+
306
+ expect(result.appUrl).toBe("https://app.test");
307
+
308
+ // Should not call open since using cached checkout
309
+ expect(mockOpenDefault).not.toHaveBeenCalled();
310
+ });
311
+
312
+ test("clears checkout ID when cache check fails", async () => {
313
+ // Mock successful responses for the complete flow after cache check fails
314
+ let callCount = 0;
315
+ global.fetch = mock(async (url) => {
316
+ callCount++;
317
+
318
+ // First call: cache check fails
319
+ if (callCount === 1 && url.includes("/status")) {
320
+ throw new Error("Network error during cache check");
321
+ }
322
+
323
+ // Create payment session
324
+ if (url.includes("/api/checkout-sessions/start")) {
325
+ return {
326
+ ok: true,
327
+ status: 200,
328
+ json: async () => ({
329
+ checkoutSession: { id: "new-checkout-123" },
330
+ paymentUrl: "https://payment.test/new-checkout-123",
331
+ }),
332
+ };
333
+ }
334
+
335
+ // Subsequent status checks and detail calls
336
+ if (url.includes("/status")) {
337
+ return {
338
+ ok: true,
339
+ status: 200,
340
+ json: async () => ({
341
+ payment_status: "paid",
342
+ vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
343
+ }),
344
+ };
345
+ }
346
+
347
+ if (url.includes("/detail")) {
348
+ return {
349
+ ok: true,
350
+ status: 200,
351
+ json: async () => ({
352
+ vendors: [
353
+ {
354
+ appUrl: "https://app.test",
355
+ homeUrl: "https://home.test",
356
+ token: "auth-token-123",
357
+ },
358
+ ],
359
+ }),
360
+ };
361
+ }
362
+ });
363
+
364
+ // Call deploy with invalid cached checkout ID - should clear it and create new one
365
+ const result = await deploy("invalid-checkout-id");
366
+
367
+ expect(result.appUrl).toBe("https://app.test");
368
+
369
+ // Verify that checkoutId was cleared due to cache check failure
370
+ expect(saveValueToConfigSpy).toHaveBeenCalledWith(
371
+ "checkoutId",
372
+ "",
373
+ "Checkout ID for document deployment service",
374
+ );
375
+ });
376
+ });
@@ -1307,7 +1307,9 @@ describe("init", () => {
1307
1307
  if (options.message.includes("[1/8]") && options.validate) {
1308
1308
  // Test the validation function directly
1309
1309
  const validationResult = options.validate([]);
1310
- expect(validationResult).toBe("Please select at least one purpose.");
1310
+ expect(validationResult).toBe(
1311
+ "Please choose at least one goal for your documentation.",
1312
+ );
1311
1313
  validateCalled = true;
1312
1314
  // Return valid result after testing validation
1313
1315
  return Promise.resolve(["getStarted"]);
@@ -1346,7 +1348,7 @@ describe("init", () => {
1346
1348
  if (options.message.includes("[2/8]") && options.validate) {
1347
1349
  // Test the validation function for target audience
1348
1350
  const validationResult = options.validate([]);
1349
- expect(validationResult).toBe("Please select at least one audience.");
1351
+ expect(validationResult).toBe("Please choose at least one audience.");
1350
1352
  audienceValidateCalled = true;
1351
1353
  return Promise.resolve(["developers"]); // Valid result after testing
1352
1354
  }
@@ -1385,11 +1387,11 @@ describe("init", () => {
1385
1387
  if (options.message.includes("Which is most important?") && options.validate) {
1386
1388
  // Test validation for empty selection
1387
1389
  let validationResult = options.validate([]);
1388
- expect(validationResult).toBe("Please select at least one priority.");
1390
+ expect(validationResult).toBe("Please choose at least one priority.");
1389
1391
 
1390
1392
  // Test validation for too many selections
1391
1393
  validationResult = options.validate(["getStarted", "completeTasks", "findAnswers"]);
1392
- expect(validationResult).toBe("Please select maximum 2 priorities.");
1394
+ expect(validationResult).toBe("Please choose maximum 2 priorities.");
1393
1395
 
1394
1396
  // Test validation for valid selection
1395
1397
  validationResult = options.validate(["getStarted", "completeTasks"]);
@@ -88,7 +88,7 @@ export async function getAccessToken(appUrl, ltToken = "") {
88
88
  const result = await createConnect({
89
89
  connectUrl: connectUrl,
90
90
  connectAction: "gen-simple-access-key",
91
- source: `AIGNE DocSmith connect to Discuss Kit`,
91
+ source: `AIGNE DocSmith connect to website`,
92
92
  closeOnSuccess: true,
93
93
  appName: "AIGNE DocSmith",
94
94
  appLogo: "https://docsmith.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
package/utils/deploy.mjs CHANGED
@@ -116,8 +116,8 @@ export async function deploy(id, cachedUrl) {
116
116
  console.log(`${chalk.blue("⏳")} Step 1/4: Waiting for payment...`);
117
117
  console.log(`${chalk.blue("🔗")} Payment link: ${chalk.cyan(paymentUrl)}\n`);
118
118
  await pollPaymentStatus(checkoutId);
119
- saveValueToConfig("checkoutId", checkoutId, "Checkout ID for document deployment service");
120
- saveValueToConfig("paymentUrl", paymentUrl, "Payment URL for document deployment service");
119
+ await saveValueToConfig("checkoutId", checkoutId, "Checkout ID for document deployment service");
120
+ await saveValueToConfig("paymentUrl", paymentUrl, "Payment URL for document deployment service");
121
121
 
122
122
  // Step 3: Wait for service installation
123
123
  console.log(`${chalk.blue("📦")} Step 2/4: Installing service...`);
@@ -174,7 +174,7 @@ async function checkCacheCheckoutId(checkoutId) {
174
174
 
175
175
  return isPaid ? checkoutId : "";
176
176
  } catch (_error) {
177
- saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
177
+ await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
178
178
  return "";
179
179
  }
180
180
  }