@aigne/doc-smith 0.8.12-beta.6 → 0.8.12-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.aigne/doc-smith/history.yaml +37 -0
  2. package/.aigne/doc-smith/preferences.yml +12 -0
  3. package/.aigne/doc-smith/upload-cache.yaml +30 -0
  4. package/.release-please-manifest.json +1 -1
  5. package/CHANGELOG.md +12 -0
  6. package/agents/generate/update-document-structure.yaml +9 -3
  7. package/agents/history/view.mjs +5 -2
  8. package/agents/update/generate-document.yaml +10 -4
  9. package/agents/update/update-document-detail.yaml +9 -3
  10. package/agents/update/user-review-document.mjs +2 -1
  11. package/docs/guides-cleaning-up.ja.md +16 -15
  12. package/docs/guides-cleaning-up.md +5 -4
  13. package/docs/guides-cleaning-up.zh-TW.md +16 -15
  14. package/docs/guides-cleaning-up.zh.md +12 -11
  15. package/docs/guides-generating-documentation.ja.md +34 -32
  16. package/docs/guides-generating-documentation.md +15 -13
  17. package/docs/guides-generating-documentation.zh-TW.md +34 -32
  18. package/docs/guides-generating-documentation.zh.md +30 -28
  19. package/docs/guides-managing-history.ja.md +17 -20
  20. package/docs/guides-managing-history.md +5 -8
  21. package/docs/guides-managing-history.zh-TW.md +18 -21
  22. package/docs/guides-managing-history.zh.md +13 -16
  23. package/docs/guides-translating-documentation.ja.md +17 -17
  24. package/docs/guides-translating-documentation.md +1 -1
  25. package/docs/guides-translating-documentation.zh-TW.md +21 -21
  26. package/docs/guides-translating-documentation.zh.md +18 -18
  27. package/docs/guides-updating-documentation.ja.md +35 -35
  28. package/docs/guides-updating-documentation.md +4 -4
  29. package/docs/guides-updating-documentation.zh-TW.md +27 -27
  30. package/docs/guides-updating-documentation.zh.md +26 -26
  31. package/docs/overview.ja.md +13 -13
  32. package/docs/overview.md +1 -1
  33. package/docs/overview.zh-TW.md +19 -19
  34. package/docs/overview.zh.md +16 -16
  35. package/package.json +1 -1
  36. package/prompts/common/afs/afs-tools-usage.md +5 -0
  37. package/prompts/common/afs/use-afs-instruction.md +1 -0
  38. package/prompts/detail/generate/system-prompt.md +0 -13
  39. package/prompts/detail/generate/user-prompt.md +7 -0
  40. package/prompts/detail/update/system-prompt.md +1 -2
  41. package/prompts/detail/update/user-prompt.md +7 -0
  42. package/prompts/structure/generate/system-prompt.md +0 -19
  43. package/prompts/structure/generate/user-prompt.md +22 -1
  44. package/prompts/structure/update/system-prompt.md +0 -17
  45. package/prompts/structure/update/user-prompt.md +24 -0
  46. package/tests/agents/history/view.test.mjs +97 -0
  47. package/tests/utils/history-utils.test.mjs +125 -97
  48. package/utils/file-utils.mjs +1 -1
  49. package/utils/history-utils.mjs +3 -3
  50. package/agents/update/fs-tools/glob.mjs +0 -184
  51. package/agents/update/fs-tools/grep.mjs +0 -317
  52. package/agents/update/fs-tools/read-file.mjs +0 -309
  53. package/tests/agents/update/fs-tools/glob.test.mjs +0 -438
  54. package/tests/agents/update/fs-tools/grep.test.mjs +0 -279
  55. package/tests/agents/update/fs-tools/read-file.test.mjs +0 -549
@@ -1,549 +0,0 @@
1
- import { afterEach, beforeEach, describe, it } from "bun:test";
2
- import assert from "node:assert";
3
- import fs from "node:fs/promises";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import readFile from "../../../../agents/update/fs-tools/read-file.mjs";
7
-
8
- describe("read-file tool", () => {
9
- let tempDir;
10
-
11
- beforeEach(async () => {
12
- // Create temporary directory for test files
13
- tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "read-file-test-"));
14
- process.chdir(tempDir);
15
- });
16
-
17
- afterEach(async () => {
18
- // Clean up temporary directory
19
- await fs.rm(tempDir, { recursive: true, force: true });
20
- process.chdir(__dirname);
21
- });
22
-
23
- describe("basic functionality", () => {
24
- it("should read a simple text file", async () => {
25
- const filePath = path.join(tempDir, "test.txt");
26
- const content = "Hello, World!\nThis is a test file.\nLine 3.";
27
- await fs.writeFile(filePath, content, "utf8");
28
-
29
- const result = await readFile({
30
- path: "test.txt",
31
- });
32
-
33
- assert.strictEqual(result.command, "read_file");
34
- assert.strictEqual(result.error, null);
35
- assert.strictEqual(result.result.content, content);
36
- assert.strictEqual(
37
- await fs.realpath(result.result.metadata.path),
38
- await fs.realpath(filePath),
39
- );
40
- assert.strictEqual(result.result.metadata.mimeType, "text/plain");
41
- assert.strictEqual(result.result.metadata.isBinary, false);
42
- assert.strictEqual(result.result.metadata.encoding, "utf8");
43
- assert.strictEqual(result.result.metadata.lineCount, 3);
44
- });
45
-
46
- it("should support backwards compatibility with path parameter", async () => {
47
- const filePath = path.join(tempDir, "compat.txt");
48
- await fs.writeFile(filePath, "Compatible content", "utf8");
49
-
50
- const result = await readFile({
51
- path: filePath,
52
- });
53
-
54
- assert.strictEqual(result.error, null);
55
- assert.strictEqual(result.result.content, "Compatible content");
56
- assert.strictEqual(result.arguments.path, filePath);
57
- });
58
-
59
- it("should read JavaScript files with correct MIME type", async () => {
60
- const filePath = path.join(tempDir, "script.js");
61
- const content = 'function hello() {\n return "world";\n}';
62
- await fs.writeFile(filePath, content, "utf8");
63
-
64
- const result = await readFile({
65
- path: filePath,
66
- });
67
-
68
- assert.strictEqual(result.error, null);
69
- assert.strictEqual(result.result.content, content);
70
- assert.strictEqual(result.result.metadata.mimeType, "text/javascript");
71
- assert.strictEqual(result.result.metadata.lineCount, 3);
72
- });
73
-
74
- it("should read JSON files with correct MIME type", async () => {
75
- const filePath = path.join(tempDir, "data.json");
76
- const content = '{\n "name": "test",\n "version": "1.0.0"\n}';
77
- await fs.writeFile(filePath, content, "utf8");
78
-
79
- const result = await readFile({
80
- path: filePath,
81
- });
82
-
83
- assert.strictEqual(result.error, null);
84
- assert.strictEqual(result.result.content, content);
85
- assert.strictEqual(result.result.metadata.mimeType, "application/json");
86
- });
87
-
88
- it("should handle empty files", async () => {
89
- const filePath = path.join(tempDir, "empty.txt");
90
- await fs.writeFile(filePath, "", "utf8");
91
-
92
- const result = await readFile({
93
- path: filePath,
94
- });
95
-
96
- assert.strictEqual(result.error, null);
97
- assert.strictEqual(result.result.content, "");
98
- assert.strictEqual(result.result.metadata.lineCount, 1); // Empty file has 1 line
99
- });
100
- });
101
-
102
- describe("offset and limit functionality", () => {
103
- let multiLineFile;
104
-
105
- beforeEach(async () => {
106
- multiLineFile = path.join(tempDir, "multiline.txt");
107
- const lines = [];
108
- for (let i = 1; i <= 20; i++) {
109
- lines.push(`Line ${i}: This is line number ${i}`);
110
- }
111
- await fs.writeFile(multiLineFile, lines.join("\n"), "utf8");
112
- });
113
-
114
- it("should read specific lines with offset and limit", async () => {
115
- const result = await readFile({
116
- path: multiLineFile,
117
- offset: 5,
118
- limit: 3,
119
- });
120
-
121
- assert.strictEqual(result.error, null);
122
- assert(result.result.content.includes("Line 6:"));
123
- assert(result.result.content.includes("Line 7:"));
124
- assert(result.result.content.includes("Line 8:"));
125
- assert(!result.result.content.includes("Line 5:"));
126
- assert(!result.result.content.includes("Line 9:"));
127
-
128
- assert.strictEqual(result.result.truncated.isTruncated, true);
129
- assert.deepStrictEqual(result.result.truncated.linesShown, [6, 8]);
130
- assert.strictEqual(result.result.truncated.totalLines, 20);
131
- assert(result.result.message.includes("truncated"));
132
- });
133
-
134
- it("should read from offset to end when no limit specified", async () => {
135
- const result = await readFile({
136
- path: multiLineFile,
137
- offset: 15,
138
- });
139
-
140
- assert.strictEqual(result.error, null);
141
- assert(result.result.content.includes("Line 16:"));
142
- assert(result.result.content.includes("Line 20:"));
143
- assert(!result.result.content.includes("Line 15:"));
144
-
145
- assert.strictEqual(result.result.truncated.isTruncated, true);
146
- assert.deepStrictEqual(result.result.truncated.linesShown, [16, 20]);
147
- });
148
-
149
- it("should handle offset beyond file end", async () => {
150
- const result = await readFile({
151
- path: multiLineFile,
152
- offset: 25,
153
- limit: 5,
154
- });
155
-
156
- assert.strictEqual(result.error, null);
157
- assert.strictEqual(result.result.content, "");
158
- assert(result.result.message.includes("beyond file end"));
159
- });
160
-
161
- it("should read first N lines with limit only", async () => {
162
- const result = await readFile({
163
- path: multiLineFile,
164
- limit: 5,
165
- });
166
-
167
- assert.strictEqual(result.error, null);
168
- assert(result.result.content.includes("Line 1:"));
169
- assert(result.result.content.includes("Line 5:"));
170
- assert(!result.result.content.includes("Line 6:"));
171
-
172
- assert.strictEqual(result.result.truncated.isTruncated, true);
173
- assert.deepStrictEqual(result.result.truncated.linesShown, [1, 5]);
174
- });
175
- });
176
-
177
- describe("binary file handling", () => {
178
- it("should detect binary files by null bytes", async () => {
179
- const filePath = path.join(tempDir, "binary.bin");
180
- const binaryData = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x00, 0x0a, 0x1a, 0x0a]); // PNG header with null byte
181
- await fs.writeFile(filePath, binaryData);
182
-
183
- const result = await readFile({
184
- path: filePath,
185
- });
186
-
187
- assert.strictEqual(result.error, null);
188
- assert.strictEqual(result.result.content, "[Binary file: binary.bin]");
189
- assert.strictEqual(result.result.metadata.isBinary, true);
190
- assert.strictEqual(result.result.metadata.encoding, null);
191
- assert.strictEqual(result.result.metadata.lineCount, null);
192
- });
193
-
194
- it("should detect image files by extension", async () => {
195
- const filePath = path.join(tempDir, "image.png");
196
- await fs.writeFile(filePath, "fake png content", "utf8");
197
-
198
- const result = await readFile({
199
- path: filePath,
200
- });
201
-
202
- assert.strictEqual(result.error, null);
203
- assert.strictEqual(result.result.content, "[Binary file: image.png]");
204
- assert.strictEqual(result.result.metadata.mimeType, "image/png");
205
- assert.strictEqual(result.result.metadata.isBinary, true);
206
- });
207
-
208
- it("should detect PDF files by extension", async () => {
209
- const filePath = path.join(tempDir, "document.pdf");
210
- await fs.writeFile(filePath, "fake pdf content", "utf8");
211
-
212
- const result = await readFile({
213
- path: filePath,
214
- });
215
-
216
- assert.strictEqual(result.error, null);
217
- assert.strictEqual(result.result.content, "[Binary file: document.pdf]");
218
- assert.strictEqual(result.result.metadata.mimeType, "application/pdf");
219
- assert.strictEqual(result.result.metadata.isBinary, true);
220
- });
221
-
222
- it("should handle large files as binary", async () => {
223
- const filePath = path.join(tempDir, "large.txt");
224
- // Create a file larger than 10MB
225
- const largeContent = "x".repeat(11 * 1024 * 1024);
226
- await fs.writeFile(filePath, largeContent, "utf8");
227
-
228
- const result = await readFile({
229
- path: filePath,
230
- });
231
-
232
- assert.strictEqual(result.error, null);
233
- assert.strictEqual(result.result.content, "[Binary file: large.txt]");
234
- assert.strictEqual(result.result.metadata.isBinary, true);
235
- });
236
- });
237
-
238
- describe("MIME type detection", () => {
239
- const testCases = [
240
- { ext: ".md", mimeType: "text/markdown" },
241
- { ext: ".ts", mimeType: "text/typescript" },
242
- { ext: ".jsx", mimeType: "text/javascript" },
243
- { ext: ".py", mimeType: "text/x-python" },
244
- { ext: ".css", mimeType: "text/css" },
245
- { ext: ".html", mimeType: "text/html" },
246
- { ext: ".yaml", mimeType: "text/yaml" },
247
- { ext: ".sh", mimeType: "text/x-shellscript" },
248
- { ext: ".jpg", mimeType: "image/jpeg" },
249
- { ext: ".gif", mimeType: "image/gif" },
250
- { ext: ".unknown", mimeType: "application/octet-stream" },
251
- ];
252
-
253
- testCases.forEach(({ ext, mimeType }) => {
254
- it(`should detect ${mimeType} for ${ext} files`, async () => {
255
- const filePath = path.join(tempDir, `test${ext}`);
256
-
257
- if (mimeType.startsWith("image/")) {
258
- // For image files, just create an empty file (will be treated as binary)
259
- await fs.writeFile(filePath, "", "utf8");
260
- } else {
261
- // For text files, create with content
262
- await fs.writeFile(filePath, "test content", "utf8");
263
- }
264
-
265
- const result = await readFile({
266
- path: filePath,
267
- });
268
-
269
- assert.strictEqual(result.error, null);
270
- assert.strictEqual(result.result.metadata.mimeType, mimeType);
271
- });
272
- });
273
- });
274
-
275
- describe("encoding support", () => {
276
- it("should read files with different encodings", async () => {
277
- const filePath = path.join(tempDir, "encoded.txt");
278
- const content = "Hello, 世界! 🌍";
279
- await fs.writeFile(filePath, content, "utf8");
280
-
281
- const result = await readFile({
282
- path: filePath,
283
- encoding: "utf8",
284
- });
285
-
286
- assert.strictEqual(result.error, null);
287
- assert.strictEqual(result.result.content, content);
288
- assert.strictEqual(result.result.metadata.encoding, "utf8");
289
- });
290
-
291
- it("should handle latin1 encoding", async () => {
292
- const filePath = path.join(tempDir, "latin1.txt");
293
- const content = "Hello, résumé!";
294
- await fs.writeFile(filePath, content, "latin1");
295
-
296
- const result = await readFile({
297
- path: filePath,
298
- encoding: "latin1",
299
- });
300
-
301
- assert.strictEqual(result.error, null);
302
- assert.strictEqual(result.result.content, content);
303
- assert.strictEqual(result.result.metadata.encoding, "latin1");
304
- });
305
- });
306
-
307
- describe("auto-truncation", () => {
308
- it("should auto-truncate very large text files", async () => {
309
- const filePath = path.join(tempDir, "huge.txt");
310
- const lines = [];
311
- for (let i = 1; i <= 15000; i++) {
312
- lines.push(`Line ${i}`);
313
- }
314
- await fs.writeFile(filePath, lines.join("\n"), "utf8");
315
-
316
- const result = await readFile({
317
- path: filePath,
318
- });
319
-
320
- assert.strictEqual(result.error, null);
321
- assert.strictEqual(result.result.metadata.lineCount, 15000);
322
- assert.strictEqual(result.result.truncated.isTruncated, true);
323
- assert.deepStrictEqual(result.result.truncated.linesShown, [1, 10000]);
324
- assert(result.result.content.includes("Line 1"));
325
- assert(result.result.content.includes("Line 10000"));
326
- assert(!result.result.content.includes("Line 10001"));
327
- });
328
-
329
- it("should not truncate files under the limit", async () => {
330
- const filePath = path.join(tempDir, "medium.txt");
331
- const lines = [];
332
- for (let i = 1; i <= 5000; i++) {
333
- lines.push(`Line ${i}`);
334
- }
335
- await fs.writeFile(filePath, lines.join("\n"), "utf8");
336
-
337
- const result = await readFile({
338
- path: filePath,
339
- });
340
-
341
- assert.strictEqual(result.error, null);
342
- assert.strictEqual(result.result.metadata.lineCount, 5000);
343
- assert.strictEqual(result.result.truncated, undefined);
344
- assert(result.result.content.includes("Line 5000"));
345
- });
346
- });
347
-
348
- describe("error handling", () => {
349
- it("should require a file path", async () => {
350
- const result = await readFile({});
351
-
352
- assert.notStrictEqual(result.error, null);
353
- assert(result.error.message.includes("required"));
354
- });
355
-
356
- it("should handle non-existent files", async () => {
357
- const result = await readFile({
358
- path: path.join(tempDir, "nonexistent.txt"),
359
- });
360
-
361
- assert.notStrictEqual(result.error, null);
362
- assert(result.error.message.includes("does not exist"));
363
- });
364
-
365
- it("should handle directories instead of files", async () => {
366
- const dirPath = path.join(tempDir, "directory");
367
- await fs.mkdir(dirPath);
368
-
369
- const result = await readFile({
370
- path: dirPath,
371
- });
372
-
373
- assert.notStrictEqual(result.error, null);
374
- assert(result.error.message.includes("directory, not a file"));
375
- });
376
-
377
- it("should validate offset parameter", async () => {
378
- const filePath = path.join(tempDir, "test.txt");
379
- await fs.writeFile(filePath, "test content", "utf8");
380
-
381
- const result = await readFile({
382
- path: filePath,
383
- offset: -1,
384
- });
385
-
386
- assert.notStrictEqual(result.error, null);
387
- assert(result.error.message.includes("non-negative"));
388
- });
389
-
390
- it("should validate limit parameter", async () => {
391
- const filePath = path.join(tempDir, "test.txt");
392
- await fs.writeFile(filePath, "test content", "utf8");
393
-
394
- const result = await readFile({
395
- path: filePath,
396
- limit: 0,
397
- });
398
-
399
- assert.notStrictEqual(result.error, null);
400
- assert(result.error.message.includes("positive"));
401
- });
402
-
403
- it("should handle invalid offset type", async () => {
404
- const filePath = path.join(tempDir, "test.txt");
405
- await fs.writeFile(filePath, "test content", "utf8");
406
-
407
- const result = await readFile({
408
- path: filePath,
409
- offset: "invalid",
410
- });
411
-
412
- assert.notStrictEqual(result.error, null);
413
- assert(result.error.message.includes("non-negative number"));
414
- });
415
-
416
- it("should handle permission errors gracefully", async () => {
417
- // This test might not work on all systems, but we include it for completeness
418
- const filePath = path.join(tempDir, "test.txt");
419
- await fs.writeFile(filePath, "test content", "utf8");
420
-
421
- const result = await readFile({
422
- path: filePath,
423
- });
424
-
425
- // Should either succeed or fail gracefully
426
- assert.strictEqual(typeof result, "object");
427
- assert.strictEqual(typeof result.result, "object");
428
- });
429
- });
430
-
431
- describe("edge cases", () => {
432
- it("should handle files with only newlines", async () => {
433
- const filePath = path.join(tempDir, "newlines.txt");
434
- await fs.writeFile(filePath, "\n\n\n", "utf8");
435
-
436
- const result = await readFile({
437
- path: filePath,
438
- });
439
-
440
- assert.strictEqual(result.error, null);
441
- assert.strictEqual(result.result.content, "\n\n\n");
442
- assert.strictEqual(result.result.metadata.lineCount, 4); // 3 newlines = 4 lines
443
- });
444
-
445
- it("should handle files with mixed line endings", async () => {
446
- const filePath = path.join(tempDir, "mixed.txt");
447
- await fs.writeFile(filePath, "Line 1\nLine 2\r\nLine 3\r", "utf8");
448
-
449
- const result = await readFile({
450
- path: filePath,
451
- });
452
-
453
- assert.strictEqual(result.error, null);
454
- assert(result.result.content.includes("Line 1"));
455
- assert(result.result.content.includes("Line 2"));
456
- assert(result.result.content.includes("Line 3"));
457
- });
458
-
459
- it("should handle very long file names", async () => {
460
- const longName = `${"a".repeat(100)}.txt`;
461
- const filePath = path.join(tempDir, longName);
462
- await fs.writeFile(filePath, "content", "utf8");
463
-
464
- const result = await readFile({
465
- path: filePath,
466
- });
467
-
468
- assert.strictEqual(result.error, null);
469
- assert.strictEqual(result.result.content, "content");
470
- });
471
-
472
- it("should handle files with special characters in content", async () => {
473
- const filePath = path.join(tempDir, "special.txt");
474
- const content = "Special chars: @#$%^&*()[]{}|\\:\";'<>?,./";
475
- await fs.writeFile(filePath, content, "utf8");
476
-
477
- const result = await readFile({
478
- path: filePath,
479
- });
480
-
481
- assert.strictEqual(result.error, null);
482
- assert.strictEqual(result.result.content, content);
483
- });
484
-
485
- it("should handle zero-byte file", async () => {
486
- const filePath = path.join(tempDir, "zero.txt");
487
- await fs.writeFile(filePath, Buffer.alloc(0));
488
-
489
- const result = await readFile({
490
- path: filePath,
491
- });
492
-
493
- assert.strictEqual(result.error, null);
494
- assert.strictEqual(result.result.content, "");
495
- assert.strictEqual(result.result.metadata.fileSize, 0);
496
- });
497
- });
498
-
499
- describe("metadata validation", () => {
500
- it("should return correct file metadata", async () => {
501
- const filePath = path.join(tempDir, "metadata.txt");
502
- const content = "Line 1\nLine 2\nLine 3";
503
- await fs.writeFile(filePath, content, "utf8");
504
-
505
- const result = await readFile({
506
- path: filePath,
507
- });
508
-
509
- assert.strictEqual(result.error, null);
510
- assert.strictEqual(result.result.metadata.path, filePath);
511
- assert.strictEqual(result.result.metadata.mimeType, "text/plain");
512
- assert.strictEqual(typeof result.result.metadata.fileSize, "number");
513
- assert(result.result.metadata.fileSize > 0);
514
- assert.strictEqual(result.result.metadata.isBinary, false);
515
- assert.strictEqual(result.result.metadata.encoding, "utf8");
516
- assert.strictEqual(result.result.metadata.lineCount, 3);
517
- });
518
-
519
- it("should handle metadata for binary files", async () => {
520
- const filePath = path.join(tempDir, "binary.bin");
521
- const binaryData = Buffer.from([0x00, 0x01, 0x02, 0x03]);
522
- await fs.writeFile(filePath, binaryData);
523
-
524
- const result = await readFile({
525
- path: filePath,
526
- });
527
-
528
- assert.strictEqual(result.error, null);
529
- assert.strictEqual(result.result.metadata.isBinary, true);
530
- assert.strictEqual(result.result.metadata.encoding, null);
531
- assert.strictEqual(result.result.metadata.lineCount, null);
532
- assert.strictEqual(result.result.metadata.fileSize, 4);
533
- });
534
- });
535
- });
536
-
537
- describe("schema validation", () => {
538
- it("should have correct input schema", () => {
539
- assert.strictEqual(typeof readFile.input_schema, "object");
540
- assert.strictEqual(readFile.input_schema.type, "object");
541
- assert(Array.isArray(readFile.input_schema.required));
542
- assert(readFile.input_schema.required.includes("path"));
543
- assert.strictEqual(typeof readFile.input_schema.properties, "object");
544
- assert.strictEqual(typeof readFile.input_schema.properties.path, "object");
545
- assert.strictEqual(typeof readFile.input_schema.properties.offset, "object");
546
- assert.strictEqual(typeof readFile.input_schema.properties.limit, "object");
547
- assert.strictEqual(typeof readFile.input_schema.properties.encoding, "object");
548
- });
549
- });