rbxstudio-mcp 2.3.2 → 2.4.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.
Files changed (175) hide show
  1. package/README.md +67 -14
  2. package/dist/__tests__/bridge-service.test.js +25 -13
  3. package/dist/__tests__/bridge-service.test.js.map +1 -1
  4. package/dist/__tests__/bridge-session.test.d.ts +2 -0
  5. package/dist/__tests__/bridge-session.test.d.ts.map +1 -0
  6. package/dist/__tests__/bridge-session.test.js +171 -0
  7. package/dist/__tests__/bridge-session.test.js.map +1 -0
  8. package/dist/__tests__/chunker.test.d.ts +2 -0
  9. package/dist/__tests__/chunker.test.d.ts.map +1 -0
  10. package/dist/__tests__/chunker.test.js +201 -0
  11. package/dist/__tests__/chunker.test.js.map +1 -0
  12. package/dist/__tests__/docs-core.test.d.ts +2 -0
  13. package/dist/__tests__/docs-core.test.d.ts.map +1 -0
  14. package/dist/__tests__/docs-core.test.js +137 -0
  15. package/dist/__tests__/docs-core.test.js.map +1 -0
  16. package/dist/__tests__/docs-fetcher.test.d.ts +2 -0
  17. package/dist/__tests__/docs-fetcher.test.d.ts.map +1 -0
  18. package/dist/__tests__/docs-fetcher.test.js +173 -0
  19. package/dist/__tests__/docs-fetcher.test.js.map +1 -0
  20. package/dist/__tests__/helpers.d.ts +8 -0
  21. package/dist/__tests__/helpers.d.ts.map +1 -0
  22. package/dist/__tests__/helpers.js +23 -0
  23. package/dist/__tests__/helpers.js.map +1 -0
  24. package/dist/__tests__/http-routes.test.d.ts +2 -0
  25. package/dist/__tests__/http-routes.test.d.ts.map +1 -0
  26. package/dist/__tests__/http-routes.test.js +233 -0
  27. package/dist/__tests__/http-routes.test.js.map +1 -0
  28. package/dist/__tests__/http-server.test.js +13 -6
  29. package/dist/__tests__/http-server.test.js.map +1 -1
  30. package/dist/__tests__/integration.test.js +9 -4
  31. package/dist/__tests__/integration.test.js.map +1 -1
  32. package/dist/__tests__/semantic-search.test.d.ts +2 -0
  33. package/dist/__tests__/semantic-search.test.d.ts.map +1 -0
  34. package/dist/__tests__/semantic-search.test.js +202 -0
  35. package/dist/__tests__/semantic-search.test.js.map +1 -0
  36. package/dist/__tests__/smoke.test.js +7 -3
  37. package/dist/__tests__/smoke.test.js.map +1 -1
  38. package/dist/__tests__/studio-client.test.d.ts +2 -0
  39. package/dist/__tests__/studio-client.test.d.ts.map +1 -0
  40. package/dist/__tests__/studio-client.test.js +25 -0
  41. package/dist/__tests__/studio-client.test.js.map +1 -0
  42. package/dist/__tests__/tool-nudges.test.d.ts +2 -0
  43. package/dist/__tests__/tool-nudges.test.d.ts.map +1 -0
  44. package/dist/__tests__/tool-nudges.test.js +60 -0
  45. package/dist/__tests__/tool-nudges.test.js.map +1 -0
  46. package/dist/__tests__/tool-registry.test.d.ts +2 -0
  47. package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
  48. package/dist/__tests__/tool-registry.test.js +365 -0
  49. package/dist/__tests__/tool-registry.test.js.map +1 -0
  50. package/dist/__tests__/tools-bridge.test.d.ts +2 -0
  51. package/dist/__tests__/tools-bridge.test.d.ts.map +1 -0
  52. package/dist/__tests__/tools-bridge.test.js +396 -0
  53. package/dist/__tests__/tools-bridge.test.js.map +1 -0
  54. package/dist/__tests__/tools-docs.test.d.ts +2 -0
  55. package/dist/__tests__/tools-docs.test.d.ts.map +1 -0
  56. package/dist/__tests__/tools-docs.test.js +112 -0
  57. package/dist/__tests__/tools-docs.test.js.map +1 -0
  58. package/dist/__tests__/tools-guards.test.d.ts +2 -0
  59. package/dist/__tests__/tools-guards.test.d.ts.map +1 -0
  60. package/dist/__tests__/tools-guards.test.js +131 -0
  61. package/dist/__tests__/tools-guards.test.js.map +1 -0
  62. package/dist/__tests__/tools-runtime.test.d.ts +2 -0
  63. package/dist/__tests__/tools-runtime.test.d.ts.map +1 -0
  64. package/dist/__tests__/tools-runtime.test.js +214 -0
  65. package/dist/__tests__/tools-runtime.test.js.map +1 -0
  66. package/dist/__tests__/tools-visual.test.d.ts +2 -0
  67. package/dist/__tests__/tools-visual.test.d.ts.map +1 -0
  68. package/dist/__tests__/tools-visual.test.js +149 -0
  69. package/dist/__tests__/tools-visual.test.js.map +1 -0
  70. package/dist/bridge-service.d.ts +99 -12
  71. package/dist/bridge-service.d.ts.map +1 -1
  72. package/dist/bridge-service.js +238 -21
  73. package/dist/bridge-service.js.map +1 -1
  74. package/dist/docs/cache.d.ts +50 -0
  75. package/dist/docs/cache.d.ts.map +1 -0
  76. package/dist/docs/cache.js +123 -0
  77. package/dist/docs/cache.js.map +1 -0
  78. package/dist/docs/embeddings/chunker.d.ts +120 -0
  79. package/dist/docs/embeddings/chunker.d.ts.map +1 -0
  80. package/dist/docs/embeddings/chunker.js +395 -0
  81. package/dist/docs/embeddings/chunker.js.map +1 -0
  82. package/dist/docs/embeddings/embedder.d.ts +41 -0
  83. package/dist/docs/embeddings/embedder.d.ts.map +1 -0
  84. package/dist/docs/embeddings/embedder.js +113 -0
  85. package/dist/docs/embeddings/embedder.js.map +1 -0
  86. package/dist/docs/embeddings/index.d.ts +102 -0
  87. package/dist/docs/embeddings/index.d.ts.map +1 -0
  88. package/dist/docs/embeddings/index.js +250 -0
  89. package/dist/docs/embeddings/index.js.map +1 -0
  90. package/dist/docs/embeddings/manager.d.ts +68 -0
  91. package/dist/docs/embeddings/manager.d.ts.map +1 -0
  92. package/dist/docs/embeddings/manager.js +97 -0
  93. package/dist/docs/embeddings/manager.js.map +1 -0
  94. package/dist/docs/fetcher.d.ts +29 -0
  95. package/dist/docs/fetcher.d.ts.map +1 -0
  96. package/dist/docs/fetcher.js +244 -0
  97. package/dist/docs/fetcher.js.map +1 -0
  98. package/dist/docs/reference.d.ts +37 -0
  99. package/dist/docs/reference.d.ts.map +1 -0
  100. package/dist/docs/reference.js +108 -0
  101. package/dist/docs/reference.js.map +1 -0
  102. package/dist/docs/search.d.ts +194 -0
  103. package/dist/docs/search.d.ts.map +1 -0
  104. package/dist/docs/search.js +733 -0
  105. package/dist/docs/search.js.map +1 -0
  106. package/dist/http-server.d.ts.map +1 -1
  107. package/dist/http-server.js +52 -5
  108. package/dist/http-server.js.map +1 -1
  109. package/dist/index.d.ts +8 -9
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +35 -1035
  112. package/dist/index.js.map +1 -1
  113. package/dist/instructions.d.ts +15 -0
  114. package/dist/instructions.d.ts.map +1 -0
  115. package/dist/instructions.js +26 -0
  116. package/dist/instructions.js.map +1 -0
  117. package/dist/tools/defs/attributes.d.ts +6 -0
  118. package/dist/tools/defs/attributes.d.ts.map +1 -0
  119. package/dist/tools/defs/attributes.js +85 -0
  120. package/dist/tools/defs/attributes.js.map +1 -0
  121. package/dist/tools/defs/docs.d.ts +17 -0
  122. package/dist/tools/defs/docs.d.ts.map +1 -0
  123. package/dist/tools/defs/docs.js +151 -0
  124. package/dist/tools/defs/docs.js.map +1 -0
  125. package/dist/tools/defs/execute.d.ts +6 -0
  126. package/dist/tools/defs/execute.d.ts.map +1 -0
  127. package/dist/tools/defs/execute.js +21 -0
  128. package/dist/tools/defs/execute.js.map +1 -0
  129. package/dist/tools/defs/inspection.d.ts +7 -0
  130. package/dist/tools/defs/inspection.d.ts.map +1 -0
  131. package/dist/tools/defs/inspection.js +202 -0
  132. package/dist/tools/defs/inspection.js.map +1 -0
  133. package/dist/tools/defs/objects.d.ts +6 -0
  134. package/dist/tools/defs/objects.d.ts.map +1 -0
  135. package/dist/tools/defs/objects.js +111 -0
  136. package/dist/tools/defs/objects.js.map +1 -0
  137. package/dist/tools/defs/properties.d.ts +6 -0
  138. package/dist/tools/defs/properties.d.ts.map +1 -0
  139. package/dist/tools/defs/properties.js +71 -0
  140. package/dist/tools/defs/properties.js.map +1 -0
  141. package/dist/tools/defs/runtime.d.ts +6 -0
  142. package/dist/tools/defs/runtime.d.ts.map +1 -0
  143. package/dist/tools/defs/runtime.js +145 -0
  144. package/dist/tools/defs/runtime.js.map +1 -0
  145. package/dist/tools/defs/scripts.d.ts +18 -0
  146. package/dist/tools/defs/scripts.d.ts.map +1 -0
  147. package/dist/tools/defs/scripts.js +163 -0
  148. package/dist/tools/defs/scripts.js.map +1 -0
  149. package/dist/tools/defs/tags.d.ts +6 -0
  150. package/dist/tools/defs/tags.d.ts.map +1 -0
  151. package/dist/tools/defs/tags.js +74 -0
  152. package/dist/tools/defs/tags.js.map +1 -0
  153. package/dist/tools/defs/visual.d.ts +7 -0
  154. package/dist/tools/defs/visual.d.ts.map +1 -0
  155. package/dist/tools/defs/visual.js +208 -0
  156. package/dist/tools/defs/visual.js.map +1 -0
  157. package/dist/tools/index.d.ts +101 -25
  158. package/dist/tools/index.d.ts.map +1 -1
  159. package/dist/tools/index.js +580 -63
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/nudges.d.ts +25 -0
  162. package/dist/tools/nudges.d.ts.map +1 -0
  163. package/dist/tools/nudges.js +34 -0
  164. package/dist/tools/nudges.js.map +1 -0
  165. package/dist/tools/registry.d.ts +20 -0
  166. package/dist/tools/registry.d.ts.map +1 -0
  167. package/dist/tools/registry.js +65 -0
  168. package/dist/tools/registry.js.map +1 -0
  169. package/dist/tools/types.d.ts +24 -0
  170. package/dist/tools/types.d.ts.map +1 -0
  171. package/dist/tools/types.js +2 -0
  172. package/dist/tools/types.js.map +1 -0
  173. package/package.json +7 -6
  174. package/studio-plugin/MCPPlugin.rbxmx +3 -238
  175. package/studio-plugin/plugin.luau +2041 -365
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Chunker unit tests — no network, no model load. Pure string-in /
3
+ * Chunk[]-out testing of the heading-aware markdown and member-aware
4
+ * YAML splitters.
5
+ *
6
+ * We use the private __test__ export to test the per-format functions
7
+ * directly, then a small fixture run of the public walkChunks() to
8
+ * sanity-check the file walk.
9
+ */
10
+ import { promises as fs } from 'fs';
11
+ import * as os from 'os';
12
+ import * as path from 'path';
13
+ import { __test__, chunkAll } from '../docs/embeddings/chunker.js';
14
+ const { chunkMarkdown, chunkYaml, extractYamlField, splitOversize } = __test__;
15
+ describe('chunker: extractYamlField', () => {
16
+ test('extracts top-level scalar', () => {
17
+ expect(extractYamlField('name: Motor6D\ntype: class', 'name')).toBe('Motor6D');
18
+ });
19
+ test('strips single quotes', () => {
20
+ expect(extractYamlField("name: 'Class.Motor6D'", 'name')).toBe('Class.Motor6D');
21
+ });
22
+ test('strips double quotes', () => {
23
+ expect(extractYamlField('name: "with spaces"', 'name')).toBe('with spaces');
24
+ });
25
+ test('returns null for missing key', () => {
26
+ expect(extractYamlField('foo: bar', 'baz')).toBeNull();
27
+ });
28
+ test('handles dash-prefixed (list-entry) form', () => {
29
+ expect(extractYamlField(' - name: C0\n type: CFrame', 'name')).toBe('C0');
30
+ });
31
+ });
32
+ describe('chunker: chunkMarkdown', () => {
33
+ test('splits on top-level headings', () => {
34
+ // Each section body needs to clear MIN_CHUNK_CHARS (40) for the
35
+ // chunker to keep it — otherwise stubby sections are dropped on
36
+ // purpose (we don't want to emit empty "## Examples" stubs).
37
+ const md = `# Intro
38
+ this is the intro section with enough content to clear the minimum chunk size threshold.
39
+
40
+ # Body
41
+ the body has more text and explanation that goes well beyond the forty-character bound.
42
+
43
+ # Conclusion
44
+ closes out the document with a substantial wrap-up paragraph that is also long enough.`;
45
+ const chunks = chunkMarkdown('en-us/test.md', md);
46
+ const headings = chunks.map((c) => c.label);
47
+ expect(headings).toContain('Intro');
48
+ expect(headings).toContain('Body');
49
+ expect(headings).toContain('Conclusion');
50
+ });
51
+ test('preamble before first heading is preserved', () => {
52
+ const longBody = 'some preamble text. '.repeat(10);
53
+ const md = `${longBody}
54
+
55
+ # First
56
+ hello world hello world hello world hello world hello world`;
57
+ const chunks = chunkMarkdown('en-us/test.md', md);
58
+ const preambles = chunks.filter((c) => c.label === '<preamble>');
59
+ expect(preambles.length).toBe(1);
60
+ expect(preambles[0].text).toMatch(/some preamble text/);
61
+ });
62
+ test('ignores headings inside code fences', () => {
63
+ const md = `# Real Heading
64
+ \`\`\`lua
65
+ -- # This is not a markdown heading
66
+ local x = 1
67
+ \`\`\`
68
+ more body.`;
69
+ const chunks = chunkMarkdown('en-us/test.md', md);
70
+ expect(chunks.length).toBe(1);
71
+ expect(chunks[0].label).toBe('Real Heading');
72
+ });
73
+ test('line ranges roughly align with heading positions', () => {
74
+ // Bodies again need to clear MIN_CHUNK_CHARS or they're dropped.
75
+ const md = `# A
76
+ section A body with enough characters to satisfy the minimum chunk size requirement.
77
+ another line of body text for section A so its line range is non-trivial too.
78
+
79
+ # B
80
+ section B body with more than forty characters of text in it so it sticks.
81
+ another line of body text for section B to make the line range meaningful.`;
82
+ const chunks = chunkMarkdown('en-us/test.md', md);
83
+ const aIdx = chunks.findIndex((c) => c.label === 'A');
84
+ const bIdx = chunks.findIndex((c) => c.label === 'B');
85
+ expect(aIdx).toBeGreaterThanOrEqual(0);
86
+ expect(bIdx).toBeGreaterThanOrEqual(0);
87
+ expect(chunks[bIdx].startLine).toBeGreaterThan(chunks[aIdx].startLine);
88
+ });
89
+ test('headed sections carry their kind', () => {
90
+ const md = `# Long Heading
91
+ ${'lorem ipsum '.repeat(20)}`;
92
+ const chunks = chunkMarkdown('en-us/test.md', md);
93
+ expect(chunks.every((c) => c.kind === 'md-section')).toBe(true);
94
+ });
95
+ });
96
+ describe('chunker: chunkYaml', () => {
97
+ test('splits properties into per-member chunks', () => {
98
+ const yaml = `name: Motor6D
99
+ type: class
100
+ summary: |
101
+ A Motor6D is a joint that drives rotation between two parts.
102
+ description: |
103
+ Motor6D is the workhorse of character animation.
104
+ properties:
105
+ - name: C0
106
+ summary: |
107
+ The CFrame offset of the joint on Part0.
108
+ type: CFrame
109
+ - name: C1
110
+ summary: |
111
+ The CFrame offset of the joint on Part1.
112
+ type: CFrame
113
+ methods:
114
+ - name: SetDesiredAngle
115
+ summary: |
116
+ Sets the angle the motor should rotate toward.
117
+ `;
118
+ const chunks = chunkYaml('en-us/reference/engine/classes/Motor6D.yaml', yaml);
119
+ const labels = chunks.map((c) => c.label);
120
+ // Preamble + 2 properties + 1 method = 4 chunks (Motor6D as label).
121
+ expect(labels).toContain('Motor6D');
122
+ expect(labels).toContain('Motor6D.C0');
123
+ expect(labels).toContain('Motor6D.C1');
124
+ expect(labels).toContain('Motor6D.SetDesiredAngle');
125
+ // Each member chunk carries the parent name in its text.
126
+ const c0 = chunks.find((c) => c.label === 'Motor6D.C0');
127
+ expect(c0.text).toMatch(/Motor6D/);
128
+ expect(c0.text).toMatch(/C0/);
129
+ expect(c0.kind).toBe('yaml-member');
130
+ });
131
+ test('non-schema yaml falls back to fallback chunker', () => {
132
+ const yaml = `# this is just yaml frontmatter
133
+ key: value
134
+ another: thing
135
+ `;
136
+ const chunks = chunkYaml('en-us/misc.yaml', yaml);
137
+ // Without `properties:` etc the file is too small to chunk at all
138
+ // (below MIN_CHUNK_CHARS) — that's fine, the test asserts no crash
139
+ // and zero or more chunks with yaml-misc kind if produced.
140
+ for (const c of chunks) {
141
+ expect(c.kind).toBe('yaml-misc');
142
+ }
143
+ });
144
+ });
145
+ describe('chunker: splitOversize', () => {
146
+ test('keeps small text intact', () => {
147
+ const out = splitOversize('hello world', 0, 0);
148
+ expect(out.length).toBe(1);
149
+ expect(out[0].text).toBe('hello world');
150
+ });
151
+ test('splits oversize on paragraph boundaries', () => {
152
+ const para = 'x'.repeat(800);
153
+ const text = `${para}\n\n${para}\n\n${para}`;
154
+ const out = splitOversize(text, 0, 10);
155
+ expect(out.length).toBeGreaterThanOrEqual(2);
156
+ for (const piece of out) {
157
+ expect(piece.text.length).toBeLessThanOrEqual(900 + 10); // some slack
158
+ }
159
+ });
160
+ });
161
+ describe('chunker: chunkAll on tiny fixture tree', () => {
162
+ let tmpDir;
163
+ beforeAll(async () => {
164
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rbxdocs-chunker-'));
165
+ const content = path.join(tmpDir, 'content', 'en-us');
166
+ await fs.mkdir(path.join(content, 'reference', 'engine', 'classes'), { recursive: true });
167
+ await fs.mkdir(path.join(content, 'animation'), { recursive: true });
168
+ await fs.writeFile(path.join(content, 'animation', 'rigging.md'), `# Rigging guide
169
+ Long explanation about rigging. ${'lorem '.repeat(60)}
170
+ ## Anatomy
171
+ A rig has parts. ${'ipsum '.repeat(60)}`, 'utf8');
172
+ await fs.writeFile(path.join(content, 'reference', 'engine', 'classes', 'Motor6D.yaml'), `name: Motor6D
173
+ type: class
174
+ summary: |
175
+ Motor6D connects two parts with a rotational joint.
176
+ description: |
177
+ More detail about Motor6D.
178
+ properties:
179
+ - name: C0
180
+ summary: |
181
+ The CFrame offset on Part0.
182
+ type: CFrame
183
+ - name: C1
184
+ summary: |
185
+ The CFrame offset on Part1.
186
+ type: CFrame
187
+ `, 'utf8');
188
+ });
189
+ afterAll(async () => {
190
+ await fs.rm(tmpDir, { recursive: true, force: true });
191
+ });
192
+ test('produces both md-section and yaml-member chunks', async () => {
193
+ const chunks = await chunkAll(tmpDir);
194
+ const kinds = new Set(chunks.map((c) => c.kind));
195
+ expect(kinds.has('md-section')).toBe(true);
196
+ expect(kinds.has('yaml-member')).toBe(true);
197
+ // Every chunk has a non-empty path.
198
+ expect(chunks.every((c) => c.path && c.startLine > 0 && c.endLine >= c.startLine)).toBe(true);
199
+ });
200
+ });
201
+ //# sourceMappingURL=chunker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.test.js","sourceRoot":"","sources":["../../src/__tests__/chunker.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=docs-core.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-core.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/docs-core.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { promises as fs } from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ import { approxSizeOnDisk, clearContent, contentRoot, ensureCacheDir, readMeta, writeMeta, } from '../docs/cache';
5
+ import { resolveReference } from '../docs/reference';
6
+ import { listDocs, readDocFile, searchDocs } from '../docs/search';
7
+ describe('docs cache helpers', () => {
8
+ let dir;
9
+ beforeEach(async () => {
10
+ dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rbxdocs-cache-'));
11
+ });
12
+ afterEach(async () => {
13
+ await fs.rm(dir, { recursive: true, force: true });
14
+ });
15
+ test('writeMeta/readMeta round-trips current-schema metadata', async () => {
16
+ await writeMeta(dir, {
17
+ repo: 'Roblox/creator-docs',
18
+ branch: 'main',
19
+ sha: 'abc',
20
+ downloadedAt: '2026-01-01T00:00:00.000Z',
21
+ lastCheckedAt: '2026-01-01T00:00:00.000Z',
22
+ fileCount: 2,
23
+ bytesOnDisk: 10,
24
+ });
25
+ await expect(readMeta(dir)).resolves.toMatchObject({
26
+ repo: 'Roblox/creator-docs',
27
+ sha: 'abc',
28
+ schemaVersion: 1,
29
+ });
30
+ });
31
+ test('readMeta treats missing, corrupt, and stale-schema metadata as empty', async () => {
32
+ await expect(readMeta(dir)).resolves.toBeNull();
33
+ await ensureCacheDir(dir);
34
+ await fs.writeFile(path.join(dir, 'meta.json'), 'not-json', 'utf8');
35
+ await expect(readMeta(dir)).resolves.toBeNull();
36
+ await fs.writeFile(path.join(dir, 'meta.json'), JSON.stringify({ schemaVersion: 0 }), 'utf8');
37
+ await expect(readMeta(dir)).resolves.toBeNull();
38
+ });
39
+ test('clearContent removes docs content and approxSizeOnDisk ignores missing paths', async () => {
40
+ const root = contentRoot(dir);
41
+ await fs.mkdir(path.join(root, 'nested'), { recursive: true });
42
+ await fs.writeFile(path.join(root, 'nested', 'file.md'), '12345', 'utf8');
43
+ await expect(approxSizeOnDisk(root)).resolves.toBe(5);
44
+ await clearContent(dir);
45
+ await expect(approxSizeOnDisk(root)).resolves.toBe(0);
46
+ });
47
+ });
48
+ describe('docs search, listing, and reference resolution', () => {
49
+ let dir;
50
+ beforeEach(async () => {
51
+ dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rbxdocs-core-'));
52
+ const root = contentRoot(dir);
53
+ await fs.mkdir(path.join(root, 'en-us', 'reference', 'engine', 'classes'), { recursive: true });
54
+ await fs.mkdir(path.join(root, 'en-us', 'reference', 'engine', 'datatypes'), { recursive: true });
55
+ await fs.mkdir(path.join(root, 'en-us', 'guide'), { recursive: true });
56
+ await fs.writeFile(path.join(root, 'en-us', 'reference', 'engine', 'classes', 'Part.yaml'), `name: Part
57
+ type: class
58
+ summary: A part is a 3D object.
59
+ properties:
60
+ - name: Anchored
61
+ summary: Anchored prevents physics from moving the part.
62
+ `, 'utf8');
63
+ await fs.writeFile(path.join(root, 'en-us', 'reference', 'engine', 'datatypes', 'Vector3.yaml'), `name: Vector3
64
+ type: datatype
65
+ summary: Three dimensional vector.
66
+ `, 'utf8');
67
+ await fs.writeFile(path.join(root, 'en-us', 'guide', 'physics.md'), `# Physics
68
+ Set Anchored when a part should not move.
69
+ Use constraints when a body must rotate smoothly.
70
+ `, 'utf8');
71
+ });
72
+ afterEach(async () => {
73
+ await fs.rm(dir, { recursive: true, force: true });
74
+ });
75
+ test('readDocFile blocks traversal and reads safe files', async () => {
76
+ await expect(readDocFile(dir, '../meta.json')).resolves.toBeNull();
77
+ const doc = await readDocFile(dir, 'en-us/guide/physics.md');
78
+ expect(doc).toMatchObject({
79
+ path: 'en-us/guide/physics.md',
80
+ });
81
+ expect(doc?.content).toContain('Set Anchored');
82
+ });
83
+ test('listDocs sorts directories before files and paginates', async () => {
84
+ const listing = await listDocs(dir, 'en-us/reference/engine', { limit: 1 });
85
+ expect(listing).toMatchObject({
86
+ path: 'en-us/reference/engine',
87
+ totalEntries: 2,
88
+ offset: 0,
89
+ limit: 1,
90
+ truncated: true,
91
+ entries: [{ name: 'classes', type: 'dir' }],
92
+ });
93
+ await expect(listDocs(dir, '../outside')).resolves.toBeNull();
94
+ });
95
+ test('searchDocs supports literal, regex, scoped, and token-AND searches', async () => {
96
+ const literal = await searchDocs(dir, 'Anchored', {
97
+ scope: 'en-us/reference',
98
+ contextLines: 1,
99
+ semantic: false,
100
+ });
101
+ expect(literal).toMatchObject({
102
+ mode: 'literal',
103
+ totalHits: 2,
104
+ filesScanned: 2,
105
+ });
106
+ expect(literal.hits[0].context).toEqual(expect.arrayContaining([{ line: 4, text: 'properties:' }]));
107
+ const regex = await searchDocs(dir, 'Vector\\d', { useRegex: true });
108
+ expect(regex).toMatchObject({ mode: 'literal', totalHits: 1 });
109
+ const tokenAnd = await searchDocs(dir, 'rotate body smoothly', {
110
+ semantic: false,
111
+ windowLines: 2,
112
+ });
113
+ expect(tokenAnd).toMatchObject({
114
+ mode: 'token-and',
115
+ tokens: ['rotate', 'body', 'smoothly'],
116
+ totalHits: 1,
117
+ });
118
+ expect(tokenAnd.hits[0].path).toBe('en-us/guide/physics.md');
119
+ });
120
+ test('resolveReference finds categories case-insensitively and returns raw YAML', async () => {
121
+ const part = await resolveReference(dir, 'part');
122
+ expect(part).toMatchObject({
123
+ category: 'class',
124
+ name: 'Part',
125
+ path: 'en-us/reference/engine/classes/Part.yaml',
126
+ data: { name: 'Part', type: 'class' },
127
+ });
128
+ expect(part?.raw).toContain('name: Part');
129
+ const vector = await resolveReference(dir, 'Vector3', 'datatype');
130
+ expect(vector).toMatchObject({
131
+ category: 'datatype',
132
+ name: 'Vector3',
133
+ });
134
+ await expect(resolveReference(dir, 'Missing')).resolves.toBeNull();
135
+ });
136
+ });
137
+ //# sourceMappingURL=docs-core.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-core.test.js","sourceRoot":"","sources":["../../src/__tests__/docs-core.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=docs-fetcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-fetcher.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/docs-fetcher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,173 @@
1
+ import { promises as fs } from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ import { Readable } from 'stream';
5
+ import * as tar from 'tar';
6
+ import { contentRoot, readMeta, writeMeta } from '../docs/cache';
7
+ import { ensureDocsCache } from '../docs/fetcher';
8
+ function jsonResponse(body) {
9
+ return {
10
+ ok: true,
11
+ status: 200,
12
+ statusText: 'OK',
13
+ json: async () => body,
14
+ };
15
+ }
16
+ function tarballResponse(buffer) {
17
+ return {
18
+ ok: true,
19
+ status: 200,
20
+ statusText: 'OK',
21
+ body: Readable.toWeb(Readable.from(buffer)),
22
+ };
23
+ }
24
+ async function makeCreatorDocsTarball(label) {
25
+ const src = await fs.mkdtemp(path.join(os.tmpdir(), 'creator-docs-src-'));
26
+ const tarPath = path.join(src, 'docs.tar.gz');
27
+ const repo = path.join(src, 'creator-docs-main');
28
+ await fs.mkdir(path.join(repo, 'content', 'en-us', 'reference', 'engine', 'classes'), {
29
+ recursive: true,
30
+ });
31
+ await fs.mkdir(path.join(repo, 'content', 'en-us', 'animation'), { recursive: true });
32
+ await fs.mkdir(path.join(repo, 'content', 'en-us', 'unkept'), { recursive: true });
33
+ await fs.writeFile(path.join(repo, 'content', 'en-us', 'reference', 'engine', 'classes', 'Part.yaml'), `name: Part\nsummary: ${label}\n`, 'utf8');
34
+ await fs.writeFile(path.join(repo, 'content', 'en-us', 'animation', 'rigging.md'), `# Rigging ${label}\n`, 'utf8');
35
+ await fs.writeFile(path.join(repo, 'content', 'en-us', 'unkept', 'skip.md'), 'not extracted', 'utf8');
36
+ await fs.writeFile(path.join(repo, 'content', 'en-us', 'animation', 'skip.txt'), 'wrong extension', 'utf8');
37
+ await tar.c({ gzip: true, cwd: src, file: tarPath }, ['creator-docs-main']);
38
+ const buffer = await fs.readFile(tarPath);
39
+ await fs.rm(src, { recursive: true, force: true });
40
+ return buffer;
41
+ }
42
+ describe('ensureDocsCache', () => {
43
+ let cacheDir;
44
+ let firstTarball;
45
+ let secondTarball;
46
+ const originalFetch = global.fetch;
47
+ beforeAll(async () => {
48
+ cacheDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rbxdocs-fetcher-'));
49
+ process.env.RBXSTUDIO_DOCS_DIR = cacheDir;
50
+ firstTarball = await makeCreatorDocsTarball('first');
51
+ secondTarball = await makeCreatorDocsTarball('second');
52
+ });
53
+ afterAll(async () => {
54
+ global.fetch = originalFetch;
55
+ delete process.env.RBXSTUDIO_DOCS_DIR;
56
+ if (cacheDir)
57
+ await fs.rm(cacheDir, { recursive: true, force: true });
58
+ });
59
+ beforeEach(async () => {
60
+ jest.restoreAllMocks();
61
+ await fs.rm(cacheDir, { recursive: true, force: true });
62
+ await fs.mkdir(cacheDir, { recursive: true });
63
+ });
64
+ function mockFetchSequence(...responses) {
65
+ const fetchMock = jest.fn();
66
+ for (const response of responses) {
67
+ fetchMock.mockResolvedValueOnce(response);
68
+ }
69
+ global.fetch = fetchMock;
70
+ return fetchMock;
71
+ }
72
+ test('downloads, filters, extracts, writes metadata, and clears stale semantic index files', async () => {
73
+ await fs.mkdir(path.join(cacheDir, 'index'), { recursive: true });
74
+ await fs.writeFile(path.join(cacheDir, 'index', 'stale.json'), '{}', 'utf8');
75
+ const fetchMock = mockFetchSequence(jsonResponse({ sha: 'sha-first' }), tarballResponse(firstTarball));
76
+ const result = await ensureDocsCache();
77
+ expect(result).toMatchObject({
78
+ cacheDir,
79
+ action: 'first-download',
80
+ meta: {
81
+ repo: 'Roblox/creator-docs',
82
+ branch: 'main',
83
+ sha: 'sha-first',
84
+ fileCount: 2,
85
+ schemaVersion: 1,
86
+ },
87
+ });
88
+ expect(fetchMock).toHaveBeenCalledTimes(2);
89
+ await expect(fs.readFile(path.join(contentRoot(cacheDir), 'en-us', 'reference', 'engine', 'classes', 'Part.yaml'), 'utf8')).resolves.toContain('first');
90
+ await expect(fs.access(path.join(contentRoot(cacheDir), 'en-us', 'unkept', 'skip.md'))).rejects.toBeTruthy();
91
+ await expect(fs.access(path.join(cacheDir, 'index', 'stale.json'))).rejects.toBeTruthy();
92
+ });
93
+ test('uses fresh metadata without hitting the network', async () => {
94
+ await writeMeta(cacheDir, {
95
+ repo: 'Roblox/creator-docs',
96
+ branch: 'main',
97
+ sha: 'fresh-sha',
98
+ downloadedAt: new Date().toISOString(),
99
+ lastCheckedAt: new Date().toISOString(),
100
+ fileCount: 1,
101
+ bytesOnDisk: 5,
102
+ });
103
+ const fetchMock = mockFetchSequence();
104
+ const result = await ensureDocsCache();
105
+ expect(result.action).toBe('fresh');
106
+ expect(result.meta.sha).toBe('fresh-sha');
107
+ expect(fetchMock).not.toHaveBeenCalled();
108
+ });
109
+ test('short-circuits on expired metadata when upstream SHA is unchanged', async () => {
110
+ await writeMeta(cacheDir, {
111
+ repo: 'Roblox/creator-docs',
112
+ branch: 'main',
113
+ sha: 'same-sha',
114
+ downloadedAt: '2000-01-01T00:00:00.000Z',
115
+ lastCheckedAt: '2000-01-01T00:00:00.000Z',
116
+ fileCount: 1,
117
+ bytesOnDisk: 5,
118
+ });
119
+ const fetchMock = mockFetchSequence(jsonResponse({ sha: 'same-sha' }));
120
+ const result = await ensureDocsCache({ ttlMs: 0 });
121
+ const meta = await readMeta(cacheDir);
122
+ expect(result.action).toBe('sha-short-circuit');
123
+ expect(result.meta.sha).toBe('same-sha');
124
+ expect(meta?.lastCheckedAt).not.toBe('2000-01-01T00:00:00.000Z');
125
+ expect(fetchMock).toHaveBeenCalledTimes(1);
126
+ });
127
+ test('redownloads when forced or when upstream SHA changed', async () => {
128
+ await writeMeta(cacheDir, {
129
+ repo: 'Roblox/creator-docs',
130
+ branch: 'main',
131
+ sha: 'old-sha',
132
+ downloadedAt: '2000-01-01T00:00:00.000Z',
133
+ lastCheckedAt: '2000-01-01T00:00:00.000Z',
134
+ fileCount: 1,
135
+ bytesOnDisk: 5,
136
+ });
137
+ mockFetchSequence(jsonResponse({ sha: 'new-sha' }), jsonResponse({ sha: 'new-sha' }), tarballResponse(secondTarball));
138
+ const changed = await ensureDocsCache({ ttlMs: 0 });
139
+ expect(changed.action).toBe('redownloaded');
140
+ expect(changed.meta.sha).toBe('new-sha');
141
+ await expect(fs.readFile(path.join(contentRoot(cacheDir), 'en-us', 'reference', 'engine', 'classes', 'Part.yaml'), 'utf8')).resolves.toContain('second');
142
+ mockFetchSequence(jsonResponse({ sha: 'forced-sha' }), tarballResponse(firstTarball));
143
+ const forced = await ensureDocsCache({ force: true });
144
+ expect(forced.action).toBe('redownloaded');
145
+ expect(forced.meta.sha).toBe('forced-sha');
146
+ });
147
+ test('surfaces GitHub and tarball errors with actionable messages', async () => {
148
+ mockFetchSequence(jsonResponse({ sha: 'sha' }), {
149
+ ok: false,
150
+ status: 500,
151
+ statusText: 'Server Error',
152
+ json: async () => ({}),
153
+ });
154
+ await expect(ensureDocsCache()).rejects.toThrow('Tarball download failed');
155
+ await writeMeta(cacheDir, {
156
+ repo: 'Roblox/creator-docs',
157
+ branch: 'main',
158
+ sha: 'old-sha',
159
+ downloadedAt: '2000-01-01T00:00:00.000Z',
160
+ lastCheckedAt: '2000-01-01T00:00:00.000Z',
161
+ fileCount: 1,
162
+ bytesOnDisk: 5,
163
+ });
164
+ mockFetchSequence({
165
+ ok: false,
166
+ status: 403,
167
+ statusText: 'Forbidden',
168
+ json: async () => ({}),
169
+ });
170
+ await expect(ensureDocsCache({ ttlMs: 0 })).rejects.toThrow('GitHub API 403 Forbidden');
171
+ });
172
+ });
173
+ //# sourceMappingURL=docs-fetcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-fetcher.test.js","sourceRoot":"","sources":["../../src/__tests__/docs-fetcher.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { RobloxStudioTools } from '../tools/index.js';
2
+ export declare function parseToolText(result: any): any;
3
+ export declare function createMockTools(): {
4
+ tools: RobloxStudioTools;
5
+ methods: Map<string, jest.Mock>;
6
+ };
7
+ export declare function observeRejection<T>(promise: Promise<T>): Promise<unknown>;
8
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/__tests__/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAI9C;AAED,wBAAgB,eAAe,IAAI;IACjC,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;CACjC,CAeA;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAOzE"}
@@ -0,0 +1,23 @@
1
+ export function parseToolText(result) {
2
+ expect(result).toHaveProperty('content');
3
+ expect(result.content[0]).toMatchObject({ type: 'text' });
4
+ return JSON.parse(result.content[0].text);
5
+ }
6
+ export function createMockTools() {
7
+ const methods = new Map();
8
+ const tools = new Proxy({}, {
9
+ get(_target, prop) {
10
+ if (!methods.has(prop)) {
11
+ methods.set(prop, jest.fn().mockResolvedValue(`${prop}:result`));
12
+ }
13
+ return methods.get(prop);
14
+ },
15
+ });
16
+ return { tools, methods };
17
+ }
18
+ export function observeRejection(promise) {
19
+ return promise.then(() => {
20
+ throw new Error('Expected promise to reject');
21
+ }, (error) => error);
22
+ }
23
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/__tests__/helpers.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,MAAW;IACvC,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,eAAe;IAI7B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,EAAE,EACF;QACE,GAAG,CAAC,OAAO,EAAE,IAAY;YACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;KACF,CACmB,CAAC;IAEvB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,OAAmB;IACrD,OAAO,OAAO,CAAC,IAAI,CACjB,GAAG,EAAE;QACH,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=http-routes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-routes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/http-routes.test.ts"],"names":[],"mappings":""}