skyloom 1.13.5 → 1.13.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 (195) hide show
  1. package/.github/workflows/ci.yml +36 -36
  2. package/README.md +220 -159
  3. package/config/providers.yaml +39 -39
  4. package/config/skills/api_integrator/SKILL.md +15 -15
  5. package/config/skills/arch_designer/SKILL.md +13 -13
  6. package/config/skills/ci_cd_manager/SKILL.md +14 -14
  7. package/config/skills/code_analysis/SKILL.md +13 -13
  8. package/config/skills/code_generator/SKILL.md +12 -12
  9. package/config/skills/code_reviewer/SKILL.md +13 -13
  10. package/config/skills/content_writer/SKILL.md +14 -14
  11. package/config/skills/data_transformer/SKILL.md +15 -15
  12. package/config/skills/document_analysis/SKILL.md +13 -13
  13. package/config/skills/emotional_companion/SKILL.md +15 -15
  14. package/config/skills/performance_checker/SKILL.md +14 -14
  15. package/config/skills/security_auditor/SKILL.md +14 -14
  16. package/config/skills/self_evolve/SKILL.md +13 -13
  17. package/config/skills/sys_operator/SKILL.md +15 -15
  18. package/config/skills/task_planner/SKILL.md +14 -14
  19. package/config/skills/web_research/SKILL.md +14 -14
  20. package/config/skills/workflow_designer/SKILL.md +13 -13
  21. package/dist/agents/dew.js +52 -52
  22. package/dist/agents/fair.js +84 -84
  23. package/dist/agents/fog.js +30 -30
  24. package/dist/agents/frost.js +32 -32
  25. package/dist/agents/rain.js +32 -32
  26. package/dist/agents/snow.js +68 -68
  27. package/dist/cli/commands_md.d.ts +41 -0
  28. package/dist/cli/commands_md.d.ts.map +1 -0
  29. package/dist/cli/commands_md.js +140 -0
  30. package/dist/cli/commands_md.js.map +1 -0
  31. package/dist/cli/input_macros.d.ts +28 -0
  32. package/dist/cli/input_macros.d.ts.map +1 -0
  33. package/dist/cli/input_macros.js +120 -0
  34. package/dist/cli/input_macros.js.map +1 -0
  35. package/dist/cli/loom.d.ts +220 -0
  36. package/dist/cli/loom.d.ts.map +1 -0
  37. package/dist/cli/loom.js +1094 -0
  38. package/dist/cli/loom.js.map +1 -0
  39. package/dist/cli/loom_chat.d.ts +20 -0
  40. package/dist/cli/loom_chat.d.ts.map +1 -0
  41. package/dist/cli/loom_chat.js +685 -0
  42. package/dist/cli/loom_chat.js.map +1 -0
  43. package/dist/cli/main.js +310 -14
  44. package/dist/cli/main.js.map +1 -1
  45. package/dist/cli/tui.d.ts.map +1 -1
  46. package/dist/cli/tui.js +7 -1
  47. package/dist/cli/tui.js.map +1 -1
  48. package/dist/core/agent/guard.d.ts +45 -0
  49. package/dist/core/agent/guard.d.ts.map +1 -0
  50. package/dist/core/agent/guard.js +113 -0
  51. package/dist/core/agent/guard.js.map +1 -0
  52. package/dist/core/agent.d.ts +17 -0
  53. package/dist/core/agent.d.ts.map +1 -1
  54. package/dist/core/agent.js +182 -93
  55. package/dist/core/agent.js.map +1 -1
  56. package/dist/core/factory.d.ts.map +1 -1
  57. package/dist/core/factory.js +34 -2
  58. package/dist/core/factory.js.map +1 -1
  59. package/dist/core/file_checkpoint.d.ts +57 -0
  60. package/dist/core/file_checkpoint.d.ts.map +1 -0
  61. package/dist/core/file_checkpoint.js +162 -0
  62. package/dist/core/file_checkpoint.js.map +1 -0
  63. package/dist/core/hooks.d.ts +43 -0
  64. package/dist/core/hooks.d.ts.map +1 -0
  65. package/dist/core/hooks.js +110 -0
  66. package/dist/core/hooks.js.map +1 -0
  67. package/dist/core/llm.d.ts.map +1 -1
  68. package/dist/core/llm.js +15 -9
  69. package/dist/core/llm.js.map +1 -1
  70. package/dist/core/longdoc.js +5 -5
  71. package/dist/core/mcp.d.ts +16 -0
  72. package/dist/core/mcp.d.ts.map +1 -1
  73. package/dist/core/mcp.js +55 -0
  74. package/dist/core/mcp.js.map +1 -1
  75. package/dist/core/model_config.d.ts +40 -0
  76. package/dist/core/model_config.d.ts.map +1 -0
  77. package/dist/core/model_config.js +191 -0
  78. package/dist/core/model_config.js.map +1 -0
  79. package/dist/core/skill.d.ts +7 -0
  80. package/dist/core/skill.d.ts.map +1 -1
  81. package/dist/core/skill.js +47 -0
  82. package/dist/core/skill.js.map +1 -1
  83. package/dist/core/skymd.d.ts +39 -0
  84. package/dist/core/skymd.d.ts.map +1 -0
  85. package/dist/core/skymd.js +177 -0
  86. package/dist/core/skymd.js.map +1 -0
  87. package/dist/core/tool.d.ts +12 -0
  88. package/dist/core/tool.d.ts.map +1 -1
  89. package/dist/core/tool.js +30 -0
  90. package/dist/core/tool.js.map +1 -1
  91. package/dist/core/verify.d.ts +27 -0
  92. package/dist/core/verify.d.ts.map +1 -0
  93. package/dist/core/verify.js +62 -0
  94. package/dist/core/verify.js.map +1 -0
  95. package/dist/skills/loader.d.ts +22 -2
  96. package/dist/skills/loader.d.ts.map +1 -1
  97. package/dist/skills/loader.js +45 -15
  98. package/dist/skills/loader.js.map +1 -1
  99. package/dist/tools/builtin.d.ts.map +1 -1
  100. package/dist/tools/builtin.js +13 -3
  101. package/dist/tools/builtin.js.map +1 -1
  102. package/dist/tools/model_tool.d.ts +11 -0
  103. package/dist/tools/model_tool.d.ts.map +1 -0
  104. package/dist/tools/model_tool.js +71 -0
  105. package/dist/tools/model_tool.js.map +1 -0
  106. package/dist/tools/todo.d.ts +30 -0
  107. package/dist/tools/todo.d.ts.map +1 -0
  108. package/dist/tools/todo.js +78 -0
  109. package/dist/tools/todo.js.map +1 -0
  110. package/docs/AESTHETIC_DESIGN.md +152 -144
  111. package/docs/OPTIMIZATION_PLAN.md +178 -178
  112. package/package.json +1 -1
  113. package/scripts/install.js +48 -48
  114. package/scripts/link.js +10 -10
  115. package/setup.bat +79 -79
  116. package/skill-test-ty2fOA/test.md +10 -10
  117. package/src/agents/dew.ts +70 -70
  118. package/src/agents/fair.ts +102 -102
  119. package/src/agents/fog.ts +48 -48
  120. package/src/agents/frost.ts +50 -50
  121. package/src/agents/rain.ts +50 -50
  122. package/src/agents/snow.ts +239 -239
  123. package/src/cli/commands_md.ts +112 -0
  124. package/src/cli/input_macros.ts +83 -0
  125. package/src/cli/loom.ts +982 -0
  126. package/src/cli/loom_chat.ts +598 -0
  127. package/src/cli/main.ts +255 -9
  128. package/src/cli/mode.ts +58 -58
  129. package/src/cli/tui.ts +228 -222
  130. package/src/core/agent/guard.ts +134 -0
  131. package/src/core/agent/task.ts +100 -100
  132. package/src/core/agent.ts +177 -95
  133. package/src/core/arbitrate.ts +162 -162
  134. package/src/core/catalog.ts +178 -178
  135. package/src/core/checkpoint.ts +94 -94
  136. package/src/core/estimate.ts +104 -104
  137. package/src/core/evolve.ts +191 -191
  138. package/src/core/factory.ts +31 -2
  139. package/src/core/file_checkpoint.ts +136 -0
  140. package/src/core/filter.ts +103 -103
  141. package/src/core/graph.ts +156 -156
  142. package/src/core/hooks.ts +126 -0
  143. package/src/core/icons.ts +53 -53
  144. package/src/core/index.ts +37 -37
  145. package/src/core/learn.ts +146 -146
  146. package/src/core/llm.ts +15 -9
  147. package/src/core/longdoc.ts +155 -155
  148. package/src/core/mcp.ts +48 -0
  149. package/src/core/mcp_server.ts +176 -176
  150. package/src/core/model_config.ts +157 -0
  151. package/src/core/profile.ts +255 -255
  152. package/src/core/router.ts +124 -124
  153. package/src/core/sandbox.ts +142 -142
  154. package/src/core/security.ts +243 -243
  155. package/src/core/skill.ts +42 -0
  156. package/src/core/skymd.ts +143 -0
  157. package/src/core/theme.ts +65 -65
  158. package/src/core/tool.ts +30 -0
  159. package/src/core/tool_router.ts +193 -193
  160. package/src/core/vector.ts +152 -152
  161. package/src/core/verify.ts +71 -0
  162. package/src/core/workspace.ts +150 -150
  163. package/src/plugins/loader.ts +66 -66
  164. package/src/skills/loader.ts +45 -16
  165. package/src/sql.js.d.ts +29 -29
  166. package/src/tools/builtin.ts +13 -3
  167. package/src/tools/computer.ts +269 -269
  168. package/src/tools/delegate.ts +49 -49
  169. package/src/tools/model_tool.ts +74 -0
  170. package/src/tools/todo.ts +76 -0
  171. package/src/web/tts.ts +93 -93
  172. package/tests/agent.test.ts +159 -159
  173. package/tests/agent_helpers.test.ts +48 -48
  174. package/tests/bus.test.ts +121 -121
  175. package/tests/catalog.test.ts +86 -86
  176. package/tests/checkpoint_commands.test.ts +124 -0
  177. package/tests/claude_compat.test.ts +110 -0
  178. package/tests/config.test.ts +41 -41
  179. package/tests/guard.test.ts +75 -0
  180. package/tests/icons.test.ts +45 -45
  181. package/tests/loom.test.ts +248 -0
  182. package/tests/memory.test.ts +170 -170
  183. package/tests/model_config.test.ts +109 -0
  184. package/tests/router.test.ts +86 -86
  185. package/tests/schemas.test.ts +51 -51
  186. package/tests/semantic.test.ts +83 -83
  187. package/tests/setup.ts +10 -10
  188. package/tests/skill.test.ts +172 -172
  189. package/tests/skymd.test.ts +146 -0
  190. package/tests/task.test.ts +60 -60
  191. package/tests/todo_toolstats.test.ts +94 -0
  192. package/tests/tool.test.ts +108 -108
  193. package/tests/tool_router.test.ts +71 -71
  194. package/tests/tui.test.ts +67 -67
  195. package/vitest.config.ts +17 -17
@@ -1,71 +1,71 @@
1
- /**
2
- * Tests for tool subset routing.
3
- */
4
- import { describe, it, expect } from 'vitest';
5
- import { selectRelevantTools } from '../src/core/tool_router';
6
- import { ToolRegistry } from '../src/core/tool';
7
-
8
- function makeRegistry(toolSpecs: Array<[string, string]>): ToolRegistry {
9
- const r = new ToolRegistry();
10
- for (const [name, desc] of toolSpecs) {
11
- r.register({
12
- name,
13
- description: desc,
14
- parameters: [{ name: 'x', type: 'string', description: 'x' }],
15
- handler: async () => 'ok',
16
- });
17
- }
18
- return r;
19
- }
20
-
21
- describe('selectRelevantTools', () => {
22
- it('short query returns full set', () => {
23
- const r = makeRegistry(Array.from({ length: 20 }, (_, i) => [`tool_${i}`, `desc ${i}`]));
24
- const names = r.listNames();
25
- const selected = selectRelevantTools(r, names, 'ok', { topK: 5 });
26
- expect(new Set(selected)).toEqual(new Set(names));
27
- });
28
-
29
- it('small catalog returns full set', () => {
30
- const r = makeRegistry([['a', 'alpha'], ['b', 'beta']]);
31
- const names = r.listNames();
32
- const selected = selectRelevantTools(r, names, 'anything goes here', { topK: 12 });
33
- expect(new Set(selected)).toEqual(new Set(names));
34
- });
35
-
36
- it('caps at topK for large catalog', () => {
37
- const r = makeRegistry(Array.from({ length: 50 }, (_, i) => [`tool_${i}`, `desc ${i}`]));
38
- const names = r.listNames();
39
- const selected = selectRelevantTools(r, names, 'find weather data', { topK: 10 });
40
- expect(selected.length).toBeLessThanOrEqual(10);
41
- });
42
-
43
- it('relevant tools score higher', () => {
44
- const r = makeRegistry([
45
- ['read_file', 'read a file from disk'],
46
- ['write_file', 'write content to a file'],
47
- ['send_email', 'send an email message'],
48
- ['fetch_url', 'fetch a web URL'],
49
- ['query_db', 'query the database'],
50
- ]);
51
- const names = r.listNames();
52
- const selected = selectRelevantTools(r, names, 'read this config file', { topK: 3 });
53
- expect(selected).toContain('read_file');
54
- });
55
-
56
- it('mustInclude always present', () => {
57
- const r = makeRegistry(Array.from({ length: 20 }, (_, i) => [`random_${i}`, `unrelated tool ${i}`]));
58
- r.register({
59
- name: 'critical_tool',
60
- description: 'must always be available',
61
- parameters: [{ name: 'x', type: 'string', description: 'x' }],
62
- handler: async () => 'ok',
63
- });
64
- const names = r.listNames();
65
- const selected = selectRelevantTools(r, names, 'totally unrelated query', {
66
- topK: 3,
67
- mustInclude: new Set(['critical_tool']),
68
- });
69
- expect(selected).toContain('critical_tool');
70
- });
71
- });
1
+ /**
2
+ * Tests for tool subset routing.
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+ import { selectRelevantTools } from '../src/core/tool_router';
6
+ import { ToolRegistry } from '../src/core/tool';
7
+
8
+ function makeRegistry(toolSpecs: Array<[string, string]>): ToolRegistry {
9
+ const r = new ToolRegistry();
10
+ for (const [name, desc] of toolSpecs) {
11
+ r.register({
12
+ name,
13
+ description: desc,
14
+ parameters: [{ name: 'x', type: 'string', description: 'x' }],
15
+ handler: async () => 'ok',
16
+ });
17
+ }
18
+ return r;
19
+ }
20
+
21
+ describe('selectRelevantTools', () => {
22
+ it('short query returns full set', () => {
23
+ const r = makeRegistry(Array.from({ length: 20 }, (_, i) => [`tool_${i}`, `desc ${i}`]));
24
+ const names = r.listNames();
25
+ const selected = selectRelevantTools(r, names, 'ok', { topK: 5 });
26
+ expect(new Set(selected)).toEqual(new Set(names));
27
+ });
28
+
29
+ it('small catalog returns full set', () => {
30
+ const r = makeRegistry([['a', 'alpha'], ['b', 'beta']]);
31
+ const names = r.listNames();
32
+ const selected = selectRelevantTools(r, names, 'anything goes here', { topK: 12 });
33
+ expect(new Set(selected)).toEqual(new Set(names));
34
+ });
35
+
36
+ it('caps at topK for large catalog', () => {
37
+ const r = makeRegistry(Array.from({ length: 50 }, (_, i) => [`tool_${i}`, `desc ${i}`]));
38
+ const names = r.listNames();
39
+ const selected = selectRelevantTools(r, names, 'find weather data', { topK: 10 });
40
+ expect(selected.length).toBeLessThanOrEqual(10);
41
+ });
42
+
43
+ it('relevant tools score higher', () => {
44
+ const r = makeRegistry([
45
+ ['read_file', 'read a file from disk'],
46
+ ['write_file', 'write content to a file'],
47
+ ['send_email', 'send an email message'],
48
+ ['fetch_url', 'fetch a web URL'],
49
+ ['query_db', 'query the database'],
50
+ ]);
51
+ const names = r.listNames();
52
+ const selected = selectRelevantTools(r, names, 'read this config file', { topK: 3 });
53
+ expect(selected).toContain('read_file');
54
+ });
55
+
56
+ it('mustInclude always present', () => {
57
+ const r = makeRegistry(Array.from({ length: 20 }, (_, i) => [`random_${i}`, `unrelated tool ${i}`]));
58
+ r.register({
59
+ name: 'critical_tool',
60
+ description: 'must always be available',
61
+ parameters: [{ name: 'x', type: 'string', description: 'x' }],
62
+ handler: async () => 'ok',
63
+ });
64
+ const names = r.listNames();
65
+ const selected = selectRelevantTools(r, names, 'totally unrelated query', {
66
+ topK: 3,
67
+ mustInclude: new Set(['critical_tool']),
68
+ });
69
+ expect(selected).toContain('critical_tool');
70
+ });
71
+ });
package/tests/tui.test.ts CHANGED
@@ -1,67 +1,67 @@
1
- import { describe, it, expect } from "vitest";
2
- import { charWidth, visualWidth, padVisual, StreamRenderer } from "../src/cli/tui";
3
-
4
- describe("CJK-aware width", () => {
5
- it("counts ascii as 1, CJK as 2", () => {
6
- expect(charWidth("a".codePointAt(0)!)).toBe(1);
7
- expect(charWidth("雾".codePointAt(0)!)).toBe(2);
8
- expect(charWidth(",".codePointAt(0)!)).toBe(2); // fullwidth comma
9
- });
10
-
11
- it("treats control chars as width 0", () => {
12
- expect(charWidth("\r".codePointAt(0)!)).toBe(0);
13
- expect(charWidth("\n".codePointAt(0)!)).toBe(0);
14
- });
15
-
16
- it("visualWidth sums correctly and ignores ANSI", () => {
17
- expect(visualWidth("abc")).toBe(3);
18
- expect(visualWidth("雾雨")).toBe(4);
19
- expect(visualWidth("a雾b")).toBe(4);
20
- expect(visualWidth("\x1b[36m雾\x1b[39m")).toBe(2); // color codes don't count
21
- });
22
-
23
- it("padVisual pads to a visual column count", () => {
24
- expect(visualWidth(padVisual("雾", 6))).toBe(6);
25
- expect(padVisual("abc", 2)).toBe("abc"); // never truncates
26
- });
27
- });
28
-
29
- /** Capture writes from a StreamRenderer into a string. */
30
- function render(text: string, columns = 40, chunk = 3): string {
31
- let buf = "";
32
- const fakeOut = { columns, write: (s: string) => { buf += s; return true; } } as any;
33
- const r = new StreamRenderer(fakeOut, { gutter: " " });
34
- for (let i = 0; i < text.length; i += chunk) r.write(text.slice(i, i + chunk));
35
- r.flush();
36
- return buf;
37
- }
38
-
39
- describe("StreamRenderer", () => {
40
- it("prefixes every line with the gutter", () => {
41
- const out = render("hello world", 80);
42
- expect(out.startsWith(" ")).toBe(true);
43
- });
44
-
45
- it("never exceeds the content width per visual line", () => {
46
- const out = render("天空织机是一个本地优先的多智能体终端框架用于验证换行宽度限制是否生效啊", 40);
47
- const maxContent = Math.min(40 - 2 - 1, 96);
48
- for (const line of out.split("\n")) {
49
- expect(visualWidth(line)).toBeLessThanOrEqual(2 + maxContent); // gutter + content
50
- }
51
- });
52
-
53
- it("strips stray carriage returns (CRLF from providers)", () => {
54
- const out = render("line one\r\nline two", 80);
55
- expect(out.includes("\r")).toBe(false);
56
- expect(out).toContain("line one");
57
- expect(out).toContain("line two");
58
- });
59
-
60
- it("wraps English on word boundaries without splitting short words", () => {
61
- // maxCols floors at 32, so use text long enough to exceed it.
62
- const out = render("alpha beta gamma delta epsilon zeta eta theta iota kappa", 40);
63
- expect(out.split("\n").length).toBeGreaterThan(1);
64
- // no whole word should be broken across a wrap (each appears intact)
65
- for (const w of ["alpha", "epsilon", "kappa"]) expect(out).toContain(w);
66
- });
67
- });
1
+ import { describe, it, expect } from "vitest";
2
+ import { charWidth, visualWidth, padVisual, StreamRenderer } from "../src/cli/tui";
3
+
4
+ describe("CJK-aware width", () => {
5
+ it("counts ascii as 1, CJK as 2", () => {
6
+ expect(charWidth("a".codePointAt(0)!)).toBe(1);
7
+ expect(charWidth("雾".codePointAt(0)!)).toBe(2);
8
+ expect(charWidth(",".codePointAt(0)!)).toBe(2); // fullwidth comma
9
+ });
10
+
11
+ it("treats control chars as width 0", () => {
12
+ expect(charWidth("\r".codePointAt(0)!)).toBe(0);
13
+ expect(charWidth("\n".codePointAt(0)!)).toBe(0);
14
+ });
15
+
16
+ it("visualWidth sums correctly and ignores ANSI", () => {
17
+ expect(visualWidth("abc")).toBe(3);
18
+ expect(visualWidth("雾雨")).toBe(4);
19
+ expect(visualWidth("a雾b")).toBe(4);
20
+ expect(visualWidth("\x1b[36m雾\x1b[39m")).toBe(2); // color codes don't count
21
+ });
22
+
23
+ it("padVisual pads to a visual column count", () => {
24
+ expect(visualWidth(padVisual("雾", 6))).toBe(6);
25
+ expect(padVisual("abc", 2)).toBe("abc"); // never truncates
26
+ });
27
+ });
28
+
29
+ /** Capture writes from a StreamRenderer into a string. */
30
+ function render(text: string, columns = 40, chunk = 3): string {
31
+ let buf = "";
32
+ const fakeOut = { columns, write: (s: string) => { buf += s; return true; } } as any;
33
+ const r = new StreamRenderer(fakeOut, { gutter: " " });
34
+ for (let i = 0; i < text.length; i += chunk) r.write(text.slice(i, i + chunk));
35
+ r.flush();
36
+ return buf;
37
+ }
38
+
39
+ describe("StreamRenderer", () => {
40
+ it("prefixes every line with the gutter", () => {
41
+ const out = render("hello world", 80);
42
+ expect(out.startsWith(" ")).toBe(true);
43
+ });
44
+
45
+ it("never exceeds the content width per visual line", () => {
46
+ const out = render("天空织机是一个本地优先的多智能体终端框架用于验证换行宽度限制是否生效啊", 40);
47
+ const maxContent = Math.min(40 - 2 - 1, 96);
48
+ for (const line of out.split("\n")) {
49
+ expect(visualWidth(line)).toBeLessThanOrEqual(2 + maxContent); // gutter + content
50
+ }
51
+ });
52
+
53
+ it("strips stray carriage returns (CRLF from providers)", () => {
54
+ const out = render("line one\r\nline two", 80);
55
+ expect(out.includes("\r")).toBe(false);
56
+ expect(out).toContain("line one");
57
+ expect(out).toContain("line two");
58
+ });
59
+
60
+ it("wraps English on word boundaries without splitting short words", () => {
61
+ // maxCols floors at 32, so use text long enough to exceed it.
62
+ const out = render("alpha beta gamma delta epsilon zeta eta theta iota kappa", 40);
63
+ expect(out.split("\n").length).toBeGreaterThan(1);
64
+ // no whole word should be broken across a wrap (each appears intact)
65
+ for (const w of ["alpha", "epsilon", "kappa"]) expect(out).toContain(w);
66
+ });
67
+ });
package/vitest.config.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { defineConfig } from 'vitest/config';
2
- import path from 'path';
3
-
4
- export default defineConfig({
5
- test: {
6
- globals: true,
7
- environment: 'node',
8
- include: ['tests/**/*.test.ts'],
9
- setupFiles: ['tests/setup.ts'],
10
- },
11
- resolve: {
12
- alias: {
13
- '@skyloom': path.resolve(__dirname, 'src'),
14
- '@': path.resolve(__dirname, 'src'),
15
- },
16
- },
17
- });
1
+ import { defineConfig } from 'vitest/config';
2
+ import path from 'path';
3
+
4
+ export default defineConfig({
5
+ test: {
6
+ globals: true,
7
+ environment: 'node',
8
+ include: ['tests/**/*.test.ts'],
9
+ setupFiles: ['tests/setup.ts'],
10
+ },
11
+ resolve: {
12
+ alias: {
13
+ '@skyloom': path.resolve(__dirname, 'src'),
14
+ '@': path.resolve(__dirname, 'src'),
15
+ },
16
+ },
17
+ });