neo.mjs 11.10.0 → 11.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
- "lastSync": "2025-11-24T14:26:12.386Z",
3
- "releasesLastFetched": "2025-11-24T14:26:12.404Z",
2
+ "lastSync": "2025-11-29T11:15:42.912Z",
3
+ "releasesLastFetched": "2025-11-29T11:15:42.924Z",
4
4
  "pushFailures": [],
5
5
  "issues": {
6
6
  "3789": {
@@ -11366,143 +11366,206 @@
11366
11366
  },
11367
11367
  "7880": {
11368
11368
  "state": "CLOSED",
11369
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7880.md",
11369
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7880.md",
11370
11370
  "closedAt": "2025-11-23T16:15:30Z",
11371
11371
  "updatedAt": "2025-11-23T16:15:31Z",
11372
11372
  "contentHash": "8169acef7278d27b7d782fc874402b8e143cad2f2e0e9a38610674a360f492ee"
11373
11373
  },
11374
11374
  "7881": {
11375
11375
  "state": "CLOSED",
11376
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7881.md",
11376
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7881.md",
11377
11377
  "closedAt": "2025-11-23T16:31:50Z",
11378
11378
  "updatedAt": "2025-11-23T16:31:50Z",
11379
11379
  "contentHash": "f4bfbf7a355b95e1f26042e94e5caae47869f6e526ee4670350b6df03c4bd2fd"
11380
11380
  },
11381
11381
  "7882": {
11382
11382
  "state": "CLOSED",
11383
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7882.md",
11383
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7882.md",
11384
11384
  "closedAt": "2025-11-23T17:38:18Z",
11385
11385
  "updatedAt": "2025-11-23T17:38:18Z",
11386
11386
  "contentHash": "f6bfa5871edab1efc17f3f4ab7c1f8609b45114b48688215df9fd46d2f220102"
11387
11387
  },
11388
11388
  "7883": {
11389
11389
  "state": "CLOSED",
11390
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7883.md",
11390
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7883.md",
11391
11391
  "closedAt": "2025-11-23T18:26:05Z",
11392
11392
  "updatedAt": "2025-11-23T18:26:05Z",
11393
11393
  "contentHash": "28b202a1c362a8ef24e73c2579084f7113ff087a566b151c19b68a2765ebf9a1"
11394
11394
  },
11395
11395
  "7884": {
11396
11396
  "state": "CLOSED",
11397
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7884.md",
11397
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7884.md",
11398
11398
  "closedAt": "2025-11-23T18:49:25Z",
11399
11399
  "updatedAt": "2025-11-23T18:49:25Z",
11400
11400
  "contentHash": "358d6ef0a09fdb6745e79d1d5c259cab9479c2ebbcfed17edbce3312a145832c"
11401
11401
  },
11402
11402
  "7885": {
11403
11403
  "state": "CLOSED",
11404
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7885.md",
11404
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7885.md",
11405
11405
  "closedAt": "2025-11-23T19:22:42Z",
11406
11406
  "updatedAt": "2025-11-23T19:22:42Z",
11407
11407
  "contentHash": "1e45ab7db9b529eb89375445b1433e9f703266e46f106f90b0ed5f04e22a7f0e"
11408
11408
  },
11409
11409
  "7886": {
11410
11410
  "state": "CLOSED",
11411
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7886.md",
11411
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7886.md",
11412
11412
  "closedAt": "2025-11-23T19:30:41Z",
11413
11413
  "updatedAt": "2025-11-23T19:30:41Z",
11414
11414
  "contentHash": "1a543173da7fa19e74c931a87aa6df43232a8c0c5c6888fe2b3bed3a3b926bbf"
11415
11415
  },
11416
11416
  "7888": {
11417
11417
  "state": "CLOSED",
11418
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7888.md",
11418
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7888.md",
11419
11419
  "closedAt": "2025-11-24T08:49:32Z",
11420
11420
  "updatedAt": "2025-11-24T08:49:32Z",
11421
11421
  "contentHash": "60df1b84159a9062a652c47489dca2eb91e646135e3f078b6b21b2cf7258ae71"
11422
11422
  },
11423
11423
  "7889": {
11424
11424
  "state": "CLOSED",
11425
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7889.md",
11425
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7889.md",
11426
11426
  "closedAt": "2025-11-24T09:40:53Z",
11427
11427
  "updatedAt": "2025-11-24T09:40:53Z",
11428
11428
  "contentHash": "edf299eae98b2194199dc2bfe43f097ac540d6a630fb9ed1f70f120a8b850193"
11429
11429
  },
11430
11430
  "7890": {
11431
11431
  "state": "CLOSED",
11432
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7890.md",
11432
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7890.md",
11433
11433
  "closedAt": "2025-11-24T10:06:09Z",
11434
11434
  "updatedAt": "2025-11-24T10:06:09Z",
11435
11435
  "contentHash": "7bc6db7b6c13691d68f74db0fe4b56b324e438ab02749cf7463d28a3385a2ae3"
11436
11436
  },
11437
11437
  "7891": {
11438
11438
  "state": "CLOSED",
11439
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7891.md",
11439
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7891.md",
11440
11440
  "closedAt": "2025-11-24T10:56:33Z",
11441
11441
  "updatedAt": "2025-11-24T10:56:33Z",
11442
11442
  "contentHash": "a5ebc6a570291812d5e071ae18f9f3ac42b07be689aad3dad9d70a267e585dc5"
11443
11443
  },
11444
11444
  "7892": {
11445
11445
  "state": "CLOSED",
11446
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7892.md",
11446
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7892.md",
11447
11447
  "closedAt": "2025-11-24T11:25:49Z",
11448
11448
  "updatedAt": "2025-11-24T11:25:49Z",
11449
11449
  "contentHash": "2692519931cea270f9b3759bb6a28fa53cf1859d75020efd825a519bca575352"
11450
11450
  },
11451
11451
  "7893": {
11452
11452
  "state": "CLOSED",
11453
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7893.md",
11453
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7893.md",
11454
11454
  "closedAt": "2025-11-24T11:47:31Z",
11455
11455
  "updatedAt": "2025-11-24T11:47:31Z",
11456
11456
  "contentHash": "9bcfe45c5c5be4663db98966828bb98f49dd5a686df728a7730b57dd4cd74784"
11457
11457
  },
11458
11458
  "7894": {
11459
11459
  "state": "CLOSED",
11460
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7894.md",
11460
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7894.md",
11461
11461
  "closedAt": "2025-11-24T11:04:55Z",
11462
11462
  "updatedAt": "2025-11-24T11:04:55Z",
11463
11463
  "contentHash": "abdcf2011ed90cd4b33b82a693def9715da8668f0efeb01df67876ec4d6f5725"
11464
11464
  },
11465
11465
  "7895": {
11466
11466
  "state": "CLOSED",
11467
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7895.md",
11467
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7895.md",
11468
11468
  "closedAt": "2025-11-24T11:54:36Z",
11469
11469
  "updatedAt": "2025-11-24T11:54:36Z",
11470
11470
  "contentHash": "d41f3e086248d7fd849ecd16b05d43f4aeda89e853e7164b51bfb7a039e6250c"
11471
11471
  },
11472
11472
  "7896": {
11473
11473
  "state": "CLOSED",
11474
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7896.md",
11474
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7896.md",
11475
11475
  "closedAt": "2025-11-24T13:02:52Z",
11476
11476
  "updatedAt": "2025-11-24T13:07:21Z",
11477
11477
  "contentHash": "91507a7af3eebccd639a50d9634e74baa28697487fd158474ee8f2e00c5bf608"
11478
11478
  },
11479
11479
  "7897": {
11480
11480
  "state": "CLOSED",
11481
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7897.md",
11481
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7897.md",
11482
11482
  "closedAt": "2025-11-24T13:02:44Z",
11483
11483
  "updatedAt": "2025-11-24T13:07:24Z",
11484
11484
  "contentHash": "d7e76a355c2f2516cb0b22366349f3c0f6549f01acb888fd401706297311802b"
11485
11485
  },
11486
11486
  "7898": {
11487
11487
  "state": "CLOSED",
11488
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7898.md",
11488
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7898.md",
11489
11489
  "closedAt": "2025-11-24T13:38:12Z",
11490
11490
  "updatedAt": "2025-11-24T13:54:56Z",
11491
11491
  "contentHash": "3e1adfccf0f12c2b90831dfbe9046d0adb53d7dfea3ee488f7d1263df66a19c7"
11492
11492
  },
11493
11493
  "7899": {
11494
11494
  "state": "CLOSED",
11495
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7899.md",
11495
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7899.md",
11496
11496
  "closedAt": "2025-11-24T13:54:24Z",
11497
11497
  "updatedAt": "2025-11-24T13:54:24Z",
11498
11498
  "contentHash": "ac5a7624b45eefd8a7655eccc25ddc88ffb2fe2da728cd46b115e2ec4fccb166"
11499
11499
  },
11500
11500
  "7900": {
11501
11501
  "state": "CLOSED",
11502
- "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7900.md",
11502
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.10.0/issue-7900.md",
11503
11503
  "closedAt": "2025-11-24T14:17:25Z",
11504
11504
  "updatedAt": "2025-11-24T14:17:25Z",
11505
11505
  "contentHash": "ac8db64fb3980f58b7026d555d3cfbbe299aff6cdb253f260941ec95d6dacb38"
11506
+ },
11507
+ "7901": {
11508
+ "state": "CLOSED",
11509
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7901.md",
11510
+ "closedAt": "2025-11-25T14:42:12Z",
11511
+ "updatedAt": "2025-11-25T14:42:12Z",
11512
+ "contentHash": "df95d24ff0394a9df62f9e77df76da28e2e940c48c5e33dce4db074258f2ee63"
11513
+ },
11514
+ "7902": {
11515
+ "state": "CLOSED",
11516
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7902.md",
11517
+ "closedAt": "2025-11-25T15:33:44Z",
11518
+ "updatedAt": "2025-11-25T15:33:44Z",
11519
+ "contentHash": "e426d92a1d399f16a99e31e738480d93f3b8a0aeb087b700d54e679d54360819"
11520
+ },
11521
+ "7903": {
11522
+ "state": "CLOSED",
11523
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7903.md",
11524
+ "closedAt": "2025-11-25T16:16:35Z",
11525
+ "updatedAt": "2025-11-25T16:16:35Z",
11526
+ "contentHash": "d9b1081d45e5d217dca97175eb83bfea2931b2db0dd2273742eab9ce7b79dc41"
11527
+ },
11528
+ "7904": {
11529
+ "state": "CLOSED",
11530
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7904.md",
11531
+ "closedAt": "2025-11-25T17:29:44Z",
11532
+ "updatedAt": "2025-11-25T17:29:44Z",
11533
+ "contentHash": "b1cbc69a5455aa9cc284c8fca4eb777321bd3f7b1fa53737369940be50d4618f"
11534
+ },
11535
+ "7905": {
11536
+ "state": "CLOSED",
11537
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7905.md",
11538
+ "closedAt": "2025-11-25T16:58:34Z",
11539
+ "updatedAt": "2025-11-25T16:58:34Z",
11540
+ "contentHash": "19b77ac389380f988cffa09a9fe8275d941b945f1d0108be7910323e85dbccc2"
11541
+ },
11542
+ "7906": {
11543
+ "state": "CLOSED",
11544
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7906.md",
11545
+ "closedAt": "2025-11-25T17:12:28Z",
11546
+ "updatedAt": "2025-11-25T17:12:28Z",
11547
+ "contentHash": "3bc74d1d726307ce576f8fccdbacb955c37e7397c3e9455b6557c858f1403a2a"
11548
+ },
11549
+ "7907": {
11550
+ "state": "CLOSED",
11551
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7907.md",
11552
+ "closedAt": "2025-11-26T12:06:44Z",
11553
+ "updatedAt": "2025-11-26T12:06:44Z",
11554
+ "contentHash": "d90268f900a41a41cc260e28bc0706c4ebb58df375fda02078e96feb415642d3"
11555
+ },
11556
+ "7908": {
11557
+ "state": "CLOSED",
11558
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE_ARCHIVE/11.11.0/issue-7908.md",
11559
+ "closedAt": "2025-11-26T12:16:41Z",
11560
+ "updatedAt": "2025-11-26T12:16:41Z",
11561
+ "contentHash": "e1743278c16039256ce4b5b1bf426954360b3932f0cd9c2b04d41ad8dd73c3e5"
11562
+ },
11563
+ "7910": {
11564
+ "state": "OPEN",
11565
+ "path": "/Users/Shared/github/neomjs/neo/.github/ISSUE/issue-7910.md",
11566
+ "closedAt": null,
11567
+ "updatedAt": "2025-11-29T11:06:44Z",
11568
+ "contentHash": "7032459b4722cefc837924239ca8e5fe980aca20e14f651694839954d1cf21f5"
11506
11569
  }
11507
11570
  },
11508
11571
  "releases": {
@@ -12089,6 +12152,14 @@
12089
12152
  "11.9.0": {
12090
12153
  "publishedAt": "2025-11-23T12:55:07Z",
12091
12154
  "contentHash": "bad71976e8158741c3c1348ac78f473688e69ad758583802075e1556745823a7"
12155
+ },
12156
+ "11.10.0": {
12157
+ "publishedAt": "2025-11-24T14:55:45Z",
12158
+ "contentHash": "a8a57e77420ccf5b3c33c67f8378ebc4ee7e169d1bac62c051bf7900c9cef7ec"
12159
+ },
12160
+ "11.11.0": {
12161
+ "publishedAt": "2025-11-26T12:47:01Z",
12162
+ "contentHash": "a36a4eb6939c0210968c3e7bddb8225c14567b3e2941c7f2c6b70f656c9128b4"
12092
12163
  }
12093
12164
  }
12094
12165
  }
package/ServiceWorker.mjs CHANGED
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='11.10.0'
23
+ * @member {String} version='11.12.0'
24
24
  */
25
- version: '11.10.0'
25
+ version: '11.12.0'
26
26
  }
27
27
 
28
28
  /**
@@ -0,0 +1,87 @@
1
+ import Base from '../../../../../src/core/Base.mjs';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * @summary Extracts knowledge chunks from JSDoc API data.
6
+ *
7
+ * This parser transforms the structured JSON output from JSDoc (`docs/output/all.json`)
8
+ * into flattened knowledge chunks. It handles the three primary API entities:
9
+ * 1. **Classes:** Extracts descriptions and inheritance relationships.
10
+ * 2. **Configs:** Extracts member descriptions, types, and default values.
11
+ * 3. **Methods:** Extracts signatures, parameters, return types, and descriptions.
12
+ *
13
+ * It also classifies content as 'example' or 'src' based on the file path, helping
14
+ * to distinguish between library code and demo usage.
15
+ *
16
+ * @class Neo.ai.mcp.server.knowledge-base.parser.ApiParser
17
+ * @extends Neo.core.Base
18
+ * @singleton
19
+ */
20
+ class ApiParser extends Base {
21
+ static config = {
22
+ /**
23
+ * @member {String} className='Neo.ai.mcp.server.knowledge-base.parser.ApiParser'
24
+ * @protected
25
+ */
26
+ className: 'Neo.ai.mcp.server.knowledge-base.parser.ApiParser',
27
+ /**
28
+ * @member {Boolean} singleton=true
29
+ * @protected
30
+ */
31
+ singleton: true
32
+ }
33
+
34
+ /**
35
+ * Parses the JSDoc API data array into granular chunks.
36
+ * @param {Array<Object>} apiData The JSDoc output array.
37
+ * @returns {Array<Object>} An array of chunks.
38
+ */
39
+ parse(apiData) {
40
+ const chunks = [];
41
+
42
+ apiData.forEach(item => {
43
+ const sourceFile = item.meta ? path.join(item.meta.path, item.meta.filename) : 'unknown';
44
+ let chunk, type = sourceFile.includes('/examples/') ? 'example' : 'src';
45
+
46
+ if (item.kind === 'class') {
47
+ chunk = {
48
+ type,
49
+ kind : 'class',
50
+ name : item.longname,
51
+ description: item.comment,
52
+ extends : item.augments?.[0],
53
+ source : sourceFile
54
+ };
55
+ } else if (item.kind === 'member' && item.memberof) {
56
+ chunk = {
57
+ type,
58
+ kind : 'config',
59
+ className : item.memberof,
60
+ name : item.name,
61
+ description: item.description,
62
+ configType : item.type?.names.join('|') || 'unknown',
63
+ source : sourceFile
64
+ };
65
+ } else if (item.kind === 'function' && item.memberof) {
66
+ chunk = {
67
+ type,
68
+ kind : 'method',
69
+ className : item.memberof,
70
+ name : item.name,
71
+ description: item.description,
72
+ params : item.params?.map(p => ({name: p.name, type: p.type?.names.join('|')})),
73
+ returns : item.returns?.map(r => r.type?.names.join('|')).join('|'),
74
+ source : sourceFile
75
+ };
76
+ }
77
+
78
+ if (chunk) {
79
+ chunks.push(chunk);
80
+ }
81
+ });
82
+
83
+ return chunks;
84
+ }
85
+ }
86
+
87
+ export default Neo.setupClass(ApiParser);
@@ -0,0 +1,76 @@
1
+ import Base from '../../../../../src/core/Base.mjs';
2
+
3
+ const sectionsRegex = /(?=^#+\s)/m;
4
+
5
+ /**
6
+ * @summary Extracts knowledge chunks from Markdown documentation files.
7
+ *
8
+ * This parser processes Markdown files (Guides, Blogs) found in the `learn/` directory.
9
+ * Its primary goal is to split long documents into smaller, semantically distinct
10
+ * chunks based on header sections (e.g., `#`, `##`).
11
+ *
12
+ * This granular chunking improves vector search relevance by allowing queries to match
13
+ * specific sections of a guide rather than the entire document. It assigns the correct
14
+ * `type` ('guide' or 'blog') based on the file's location in the learning tree.
15
+ *
16
+ * @class Neo.ai.mcp.server.knowledge-base.parser.DocumentationParser
17
+ * @extends Neo.core.Base
18
+ * @singleton
19
+ */
20
+ class DocumentationParser extends Base {
21
+ static config = {
22
+ /**
23
+ * @member {String} className='Neo.ai.mcp.server.knowledge-base.parser.DocumentationParser'
24
+ * @protected
25
+ */
26
+ className: 'Neo.ai.mcp.server.knowledge-base.parser.DocumentationParser',
27
+ /**
28
+ * @member {Boolean} singleton=true
29
+ * @protected
30
+ */
31
+ singleton: true
32
+ }
33
+
34
+ /**
35
+ * Parses a Markdown file into granular chunks based on headers.
36
+ * @param {Object} item The item metadata from tree.json.
37
+ * @param {String} content The raw file content.
38
+ * @param {String} filePath The absolute file path.
39
+ * @returns {Array<Object>} An array of chunks.
40
+ */
41
+ parse(item, content, filePath) {
42
+ const chunks = [];
43
+ const sections = content.split(sectionsRegex);
44
+ const type = item.parentId === 'Blog' ? 'blog' : 'guide';
45
+
46
+ if (sections.length > 1) {
47
+ sections.forEach(section => {
48
+ if (section.trim() === '') return;
49
+ const headingMatch = section.match(/^#+\s(.*)/);
50
+ const chunk = {
51
+ type,
52
+ kind : 'guide',
53
+ name : `${item.name} - ${headingMatch ? headingMatch[1] : item.name}`,
54
+ id : item.id,
55
+ content: section,
56
+ source : filePath
57
+ };
58
+ chunks.push(chunk);
59
+ });
60
+ } else {
61
+ const chunk = {
62
+ type,
63
+ kind : 'guide',
64
+ name : item.name,
65
+ id : item.id,
66
+ content,
67
+ source: filePath
68
+ };
69
+ chunks.push(chunk);
70
+ }
71
+
72
+ return chunks;
73
+ }
74
+ }
75
+
76
+ export default Neo.setupClass(DocumentationParser);
@@ -0,0 +1,138 @@
1
+ import * as acorn from 'acorn';
2
+ import Base from '../../../../../src/core/Base.mjs';
3
+ import logger from '../logger.mjs';
4
+
5
+ /**
6
+ * @summary Extracts knowledge chunks from Playwright test files.
7
+ *
8
+ * This parser is responsible for decomposing Playwright test files (`.spec.mjs`) into
9
+ * granular knowledge chunks. It identifies:
10
+ * 1. **File Context:** Imports, setup calls, and top-level descriptions.
11
+ * 2. **Test Cases:** Individual `test()` blocks, extracting their description and body.
12
+ *
13
+ * It uses AST parsing (via `acorn`) to robustly handle test file structure, ensuring
14
+ * that code comments and nested blocks are correctly processed. The generated chunks
15
+ * include line number metadata to support precise navigation.
16
+ *
17
+ * @class Neo.ai.mcp.server.knowledge-base.parser.TestParser
18
+ * @extends Neo.core.Base
19
+ * @singleton
20
+ */
21
+ class TestParser extends Base {
22
+ static config = {
23
+ /**
24
+ * @member {String} className='Neo.ai.mcp.server.knowledge-base.parser.TestParser'
25
+ * @protected
26
+ */
27
+ className: 'Neo.ai.mcp.server.knowledge-base.parser.TestParser',
28
+ /**
29
+ * @member {Boolean} singleton=true
30
+ * @protected
31
+ */
32
+ singleton: true
33
+ }
34
+
35
+ /**
36
+ * Parses a Playwright test file into granular chunks.
37
+ * @param {String} content The raw file content.
38
+ * @param {String} filePath The relative file path.
39
+ * @returns {Array<Object>} An array of chunks (header + test cases).
40
+ */
41
+ parse(content, filePath) {
42
+ const chunks = [];
43
+ let ast;
44
+ try {
45
+ ast = acorn.parse(content, { sourceType: 'module', locations: true, ecmaVersion: 2022 });
46
+ } catch (e) {
47
+ logger.warn(`Failed to parse test file ${filePath}: ${e.message}`);
48
+ return [];
49
+ }
50
+
51
+ const testNodes = [];
52
+
53
+ const visit = (node) => {
54
+ if (!node) return;
55
+
56
+ // Handle ExpressionStatement (wraps calls at top level)
57
+ if (node.type === 'ExpressionStatement') {
58
+ visit(node.expression);
59
+ return;
60
+ }
61
+
62
+ // Handle CallExpression
63
+ if (node.type === 'CallExpression') {
64
+ const callee = node.callee;
65
+ // Check for test() or test.only(), test.skip(), test.fixme()
66
+ const isTest =
67
+ (callee.name === 'test' && (!callee.property || ['only', 'skip', 'fixme'].includes(callee.property?.name))) ||
68
+ (callee.object?.name === 'test' && ['only', 'skip', 'fixme'].includes(callee.property?.name));
69
+
70
+ if (isTest) {
71
+ testNodes.push(node);
72
+ return; // Do not recurse into test body
73
+ }
74
+
75
+ // Recurse into arguments to find nested tests (e.g. inside describe blocks)
76
+ if (node.arguments) {
77
+ node.arguments.forEach(arg => {
78
+ if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
79
+ visit(arg.body);
80
+ }
81
+ });
82
+ }
83
+ return;
84
+ }
85
+
86
+ // Recurse standard block structures
87
+ if (node.body) {
88
+ if (Array.isArray(node.body)) node.body.forEach(visit);
89
+ else visit(node.body);
90
+ }
91
+ };
92
+
93
+ visit(ast);
94
+
95
+ if (testNodes.length === 0) return [];
96
+
97
+ // Sort by position to find the "header" cut-off
98
+ testNodes.sort((a, b) => a.start - b.start);
99
+
100
+ // 1. Header Chunk
101
+ // Content up to the start of the first test.
102
+ // This includes imports, setup, and the opening lines of any wrapping describe blocks.
103
+ const headerEnd = testNodes[0].start;
104
+ const headerContent = content.substring(0, headerEnd).trim();
105
+
106
+ if (headerContent.length > 0) {
107
+ chunks.push({
108
+ type : 'test',
109
+ kind : 'test-header',
110
+ name : `${filePath} - [Context]`,
111
+ content: headerContent,
112
+ source : filePath
113
+ });
114
+ }
115
+
116
+ // 2. Test Chunks
117
+ testNodes.forEach(node => {
118
+ let description = 'Unknown Test';
119
+ if (node.arguments && node.arguments[0] && node.arguments[0].type === 'Literal') {
120
+ description = node.arguments[0].value;
121
+ }
122
+
123
+ chunks.push({
124
+ type : 'test',
125
+ kind : 'test-case',
126
+ name : `${filePath} - ${description}`,
127
+ content : content.substring(node.start, node.end),
128
+ source : filePath,
129
+ line_start: node.loc.start.line,
130
+ line_end : node.loc.end.line
131
+ });
132
+ });
133
+
134
+ return chunks;
135
+ }
136
+ }
137
+
138
+ export default Neo.setupClass(TestParser);