@hubblecommerce/overmind-core 0.1.3 ā 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/integrations/confluence/confluence.client.d.ts +2 -8
- package/dist/src/integrations/confluence/confluence.client.d.ts.map +1 -1
- package/dist/src/integrations/confluence/confluence.client.js +13 -40
- package/dist/src/integrations/confluence/confluence.client.test.d.ts +2 -0
- package/dist/src/integrations/confluence/confluence.client.test.d.ts.map +1 -0
- package/dist/src/integrations/confluence/confluence.client.test.js +320 -0
- package/dist/src/integrations/gitlab/gitlab.client.d.ts +8 -0
- package/dist/src/integrations/gitlab/gitlab.client.d.ts.map +1 -1
- package/dist/src/integrations/gitlab/gitlab.client.js +20 -0
- package/dist/src/integrations/gitlab/gitlab.client.test.d.ts +2 -0
- package/dist/src/integrations/gitlab/gitlab.client.test.d.ts.map +1 -0
- package/dist/src/integrations/gitlab/gitlab.client.test.js +62 -0
- package/dist/src/integrations/jira/jira.client.d.ts +3 -2
- package/dist/src/integrations/jira/jira.client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira.client.js +23 -86
- package/dist/src/integrations/jira/jira.client.test.d.ts +2 -0
- package/dist/src/integrations/jira/jira.client.test.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira.client.test.js +171 -0
- package/dist/src/llm/anthropic-retry-wrapper.test.d.ts +2 -0
- package/dist/src/llm/anthropic-retry-wrapper.test.d.ts.map +1 -0
- package/dist/src/llm/anthropic-retry-wrapper.test.js +125 -0
- package/dist/src/processors/confluence-document.processor.test.d.ts +2 -0
- package/dist/src/processors/confluence-document.processor.test.d.ts.map +1 -0
- package/dist/src/processors/confluence-document.processor.test.js +202 -0
- package/dist/src/processors/confluence-html-parser.test.d.ts +2 -0
- package/dist/src/processors/confluence-html-parser.test.d.ts.map +1 -0
- package/dist/src/processors/confluence-html-parser.test.js +214 -0
- package/dist/src/tools/confluence-search.tool.d.ts +6 -6
- package/dist/src/tools/get-current-date.tool.d.ts.map +1 -1
- package/dist/src/tools/get-current-date.tool.js +1 -5
- package/package.json +7 -2
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseConfluenceHtml } from './confluence-html-parser.js';
|
|
3
|
+
describe('parseConfluenceHtml', () => {
|
|
4
|
+
describe('basic HTML conversion', () => {
|
|
5
|
+
it('converts h2 to ## heading', () => {
|
|
6
|
+
expect(parseConfluenceHtml('<h2>My Section</h2>')).toBe('## My Section');
|
|
7
|
+
});
|
|
8
|
+
it('converts h1 to # heading', () => {
|
|
9
|
+
expect(parseConfluenceHtml('<h1>Title</h1>')).toBe('# Title');
|
|
10
|
+
});
|
|
11
|
+
it('converts strong to **bold**', () => {
|
|
12
|
+
expect(parseConfluenceHtml('<p><strong>bold text</strong></p>')).toContain('**bold text**');
|
|
13
|
+
});
|
|
14
|
+
it('converts inline code to backtick', () => {
|
|
15
|
+
expect(parseConfluenceHtml('<p><code>myFunc()</code></p>')).toContain('`myFunc()`');
|
|
16
|
+
});
|
|
17
|
+
it('converts anchor tag to markdown link', () => {
|
|
18
|
+
const result = parseConfluenceHtml('<a href="https://example.com">click here</a>');
|
|
19
|
+
expect(result).toContain('[click here](https://example.com)');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('whitespace cleanup', () => {
|
|
23
|
+
it('collapses 3+ consecutive newlines to 2', () => {
|
|
24
|
+
const html = '<p>First</p>\n\n\n\n<p>Second</p>';
|
|
25
|
+
const result = parseConfluenceHtml(html);
|
|
26
|
+
expect(result).not.toMatch(/\n{3,}/);
|
|
27
|
+
});
|
|
28
|
+
it('removes trailing spaces from lines', () => {
|
|
29
|
+
const result = parseConfluenceHtml('<p>Hello world</p>');
|
|
30
|
+
const lines = result.split('\n');
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
expect(line).toBe(line.trimEnd());
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
it('trims leading and trailing whitespace', () => {
|
|
36
|
+
const result = parseConfluenceHtml('<p>Hello</p>');
|
|
37
|
+
expect(result).toBe(result.trim());
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('Confluence code macro', () => {
|
|
41
|
+
it('renders code macro as fenced code block', () => {
|
|
42
|
+
const html = `<ac:structured-macro ac:name="code">
|
|
43
|
+
<ac:plain-text-body>const x = 1;</ac:plain-text-body>
|
|
44
|
+
</ac:structured-macro>`;
|
|
45
|
+
const result = parseConfluenceHtml(html);
|
|
46
|
+
expect(result).toContain('```');
|
|
47
|
+
expect(result).toContain('const x = 1;');
|
|
48
|
+
});
|
|
49
|
+
it('includes language when parameter is present', () => {
|
|
50
|
+
const html = `<ac:structured-macro ac:name="code">
|
|
51
|
+
<ac:parameter ac:name="language">javascript</ac:parameter>
|
|
52
|
+
<ac:plain-text-body>const x = 1;</ac:plain-text-body>
|
|
53
|
+
</ac:structured-macro>`;
|
|
54
|
+
const result = parseConfluenceHtml(html);
|
|
55
|
+
expect(result).toContain('```javascript');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('Confluence panel macros', () => {
|
|
59
|
+
it('renders info macro as blockquote with INFO label', () => {
|
|
60
|
+
const html = `<ac:structured-macro ac:name="info">
|
|
61
|
+
<ac:rich-text-body><p>Important info</p></ac:rich-text-body>
|
|
62
|
+
</ac:structured-macro>`;
|
|
63
|
+
const result = parseConfluenceHtml(html);
|
|
64
|
+
expect(result).toContain('> **INFO**');
|
|
65
|
+
});
|
|
66
|
+
it('renders warning macro as blockquote with WARNING label', () => {
|
|
67
|
+
const html = `<ac:structured-macro ac:name="warning">
|
|
68
|
+
<ac:rich-text-body><p>Be careful</p></ac:rich-text-body>
|
|
69
|
+
</ac:structured-macro>`;
|
|
70
|
+
const result = parseConfluenceHtml(html);
|
|
71
|
+
expect(result).toContain('> **WARNING**');
|
|
72
|
+
});
|
|
73
|
+
it('renders tip macro as blockquote with TIP label', () => {
|
|
74
|
+
const html = `<ac:structured-macro ac:name="tip">
|
|
75
|
+
<ac:rich-text-body><p>A helpful tip</p></ac:rich-text-body>
|
|
76
|
+
</ac:structured-macro>`;
|
|
77
|
+
const result = parseConfluenceHtml(html);
|
|
78
|
+
expect(result).toContain('> **TIP**');
|
|
79
|
+
});
|
|
80
|
+
it('renders note macro as blockquote with NOTE label', () => {
|
|
81
|
+
const html = `<ac:structured-macro ac:name="note">
|
|
82
|
+
<ac:rich-text-body><p>Take note</p></ac:rich-text-body>
|
|
83
|
+
</ac:structured-macro>`;
|
|
84
|
+
const result = parseConfluenceHtml(html);
|
|
85
|
+
expect(result).toContain('> **NOTE**');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('Confluence TOC macro', () => {
|
|
89
|
+
it('renders toc macro as Table of Contents text', () => {
|
|
90
|
+
// Real Confluence TOC macros include parameters ā an empty element is
|
|
91
|
+
// not matched by the filter in some DOM parsers
|
|
92
|
+
const html = `<ac:structured-macro ac:name="toc">
|
|
93
|
+
<ac:parameter ac:name="maxLevel">3</ac:parameter>
|
|
94
|
+
</ac:structured-macro>`;
|
|
95
|
+
const result = parseConfluenceHtml(html);
|
|
96
|
+
expect(result).toContain('**Table of Contents**');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('Confluence JIRA macro', () => {
|
|
100
|
+
it('renders jira macro with key as JIRA Issue reference', () => {
|
|
101
|
+
const html = `<ac:structured-macro ac:name="jira">
|
|
102
|
+
<ac:parameter ac:name="key">PROJ-123</ac:parameter>
|
|
103
|
+
</ac:structured-macro>`;
|
|
104
|
+
const result = parseConfluenceHtml(html);
|
|
105
|
+
expect(result).toContain('**JIRA Issue:** PROJ-123');
|
|
106
|
+
});
|
|
107
|
+
it('renders empty string when jira macro has no key', () => {
|
|
108
|
+
const html = `<ac:structured-macro ac:name="jira"></ac:structured-macro>`;
|
|
109
|
+
const result = parseConfluenceHtml(html);
|
|
110
|
+
expect(result).not.toContain('JIRA Issue');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('Confluence status macro', () => {
|
|
114
|
+
it('renders status macro as **[title]**', () => {
|
|
115
|
+
const html = `<ac:structured-macro ac:name="status">
|
|
116
|
+
<ac:parameter ac:name="title">IN PROGRESS</ac:parameter>
|
|
117
|
+
</ac:structured-macro>`;
|
|
118
|
+
const result = parseConfluenceHtml(html);
|
|
119
|
+
expect(result).toContain('**[IN PROGRESS]**');
|
|
120
|
+
});
|
|
121
|
+
it('appends color in parentheses when colour parameter is present', () => {
|
|
122
|
+
const html = `<ac:structured-macro ac:name="status">
|
|
123
|
+
<ac:parameter ac:name="title">DONE</ac:parameter>
|
|
124
|
+
<ac:parameter ac:name="colour">Green</ac:parameter>
|
|
125
|
+
</ac:structured-macro>`;
|
|
126
|
+
const result = parseConfluenceHtml(html);
|
|
127
|
+
expect(result).toContain('(Green)');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('Confluence anchor macro', () => {
|
|
131
|
+
it('renders anchor macro as named anchor tag', () => {
|
|
132
|
+
const html = `<ac:structured-macro ac:name="anchor">
|
|
133
|
+
<ac:parameter>my-anchor</ac:parameter>
|
|
134
|
+
</ac:structured-macro>`;
|
|
135
|
+
const result = parseConfluenceHtml(html);
|
|
136
|
+
expect(result).toContain('<a name="my-anchor"></a>');
|
|
137
|
+
});
|
|
138
|
+
it('renders empty string when anchor has no parameter text', () => {
|
|
139
|
+
const html = `<ac:structured-macro ac:name="anchor">
|
|
140
|
+
<ac:parameter></ac:parameter>
|
|
141
|
+
</ac:structured-macro>`;
|
|
142
|
+
const result = parseConfluenceHtml(html);
|
|
143
|
+
expect(result).not.toContain('<a name=');
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('Confluence excerpt macro', () => {
|
|
147
|
+
it('returns rich-text-body content for excerpt macro', () => {
|
|
148
|
+
const html = `<ac:structured-macro ac:name="excerpt">
|
|
149
|
+
<ac:rich-text-body><p>Excerpt content</p></ac:rich-text-body>
|
|
150
|
+
</ac:structured-macro>`;
|
|
151
|
+
const result = parseConfluenceHtml(html);
|
|
152
|
+
expect(result).toContain('Excerpt content');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe('Confluence expand macro', () => {
|
|
156
|
+
it('renders expand macro with title and body', () => {
|
|
157
|
+
const html = `<ac:structured-macro ac:name="expand">
|
|
158
|
+
<ac:parameter ac:name="title">Click to expand</ac:parameter>
|
|
159
|
+
<ac:rich-text-body><p>Hidden content</p></ac:rich-text-body>
|
|
160
|
+
</ac:structured-macro>`;
|
|
161
|
+
const result = parseConfluenceHtml(html);
|
|
162
|
+
expect(result).toContain('**Click to expand**');
|
|
163
|
+
expect(result).toContain('Hidden content');
|
|
164
|
+
});
|
|
165
|
+
it('uses "Details" as default title when no title parameter', () => {
|
|
166
|
+
const html = `<ac:structured-macro ac:name="expand">
|
|
167
|
+
<ac:rich-text-body><p>Some content</p></ac:rich-text-body>
|
|
168
|
+
</ac:structured-macro>`;
|
|
169
|
+
const result = parseConfluenceHtml(html);
|
|
170
|
+
expect(result).toContain('**Details**');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('Confluence quote macro', () => {
|
|
174
|
+
it('renders quote macro as blockquote', () => {
|
|
175
|
+
const html = `<ac:structured-macro ac:name="quote">
|
|
176
|
+
<ac:rich-text-body><p>A famous quote</p></ac:rich-text-body>
|
|
177
|
+
</ac:structured-macro>`;
|
|
178
|
+
const result = parseConfluenceHtml(html);
|
|
179
|
+
expect(result).toContain('> ');
|
|
180
|
+
expect(result).toContain('A famous quote');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('Confluence link macro', () => {
|
|
184
|
+
it('renders ac:link with ri:page as markdown link using content-title', () => {
|
|
185
|
+
const html = `<ac:link><ri:page ri:content-title="My Page"/><ac:plain-text-link-body>My Page</ac:plain-text-link-body></ac:link>`;
|
|
186
|
+
const result = parseConfluenceHtml(html);
|
|
187
|
+
expect(result).toContain('[My Page](My Page)');
|
|
188
|
+
});
|
|
189
|
+
it('renders ac:link without href as plain text', () => {
|
|
190
|
+
const html = `<ac:link><ac:plain-text-link-body>Just text</ac:plain-text-link-body></ac:link>`;
|
|
191
|
+
const result = parseConfluenceHtml(html);
|
|
192
|
+
expect(result).toContain('Just text');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('Confluence unknown macro', () => {
|
|
196
|
+
it('falls back to text content for unknown macros', () => {
|
|
197
|
+
const html = `<ac:structured-macro ac:name="unknown-macro">
|
|
198
|
+
<ac:rich-text-body><p>Fallback text</p></ac:rich-text-body>
|
|
199
|
+
</ac:structured-macro>`;
|
|
200
|
+
const result = parseConfluenceHtml(html);
|
|
201
|
+
expect(result).toContain('Fallback text');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe('Confluence JIRA macro with server', () => {
|
|
205
|
+
it('includes server name when present', () => {
|
|
206
|
+
const html = `<ac:structured-macro ac:name="jira">
|
|
207
|
+
<ac:parameter ac:name="key">PROJ-456</ac:parameter>
|
|
208
|
+
<ac:parameter ac:name="server">My Jira</ac:parameter>
|
|
209
|
+
</ac:structured-macro>`;
|
|
210
|
+
const result = parseConfluenceHtml(html);
|
|
211
|
+
expect(result).toContain('(My Jira)');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -22,7 +22,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
22
22
|
parent_page_id: z.ZodOptional<z.ZodString>;
|
|
23
23
|
}, "strip", z.ZodTypeAny, {
|
|
24
24
|
keywords?: string[] | undefined;
|
|
25
|
-
content_type?: "
|
|
25
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
26
26
|
customer?: string | undefined;
|
|
27
27
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
28
28
|
space_key?: string | undefined;
|
|
@@ -36,7 +36,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
36
36
|
parent_page_id?: string | undefined;
|
|
37
37
|
}, {
|
|
38
38
|
keywords?: string[] | undefined;
|
|
39
|
-
content_type?: "
|
|
39
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
40
40
|
customer?: string | undefined;
|
|
41
41
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
42
42
|
space_key?: string | undefined;
|
|
@@ -53,7 +53,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
53
53
|
query: string;
|
|
54
54
|
filters?: {
|
|
55
55
|
keywords?: string[] | undefined;
|
|
56
|
-
content_type?: "
|
|
56
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
57
57
|
customer?: string | undefined;
|
|
58
58
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
59
59
|
space_key?: string | undefined;
|
|
@@ -70,7 +70,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
70
70
|
query: string;
|
|
71
71
|
filters?: {
|
|
72
72
|
keywords?: string[] | undefined;
|
|
73
|
-
content_type?: "
|
|
73
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
74
74
|
customer?: string | undefined;
|
|
75
75
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
76
76
|
space_key?: string | undefined;
|
|
@@ -87,7 +87,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
87
87
|
query: string;
|
|
88
88
|
filters?: {
|
|
89
89
|
keywords?: string[] | undefined;
|
|
90
|
-
content_type?: "
|
|
90
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
91
91
|
customer?: string | undefined;
|
|
92
92
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
93
93
|
space_key?: string | undefined;
|
|
@@ -104,7 +104,7 @@ export declare function createConfluenceAgentTool(vectorStore: VectorStoreProvid
|
|
|
104
104
|
query: string;
|
|
105
105
|
filters?: {
|
|
106
106
|
keywords?: string[] | undefined;
|
|
107
|
-
content_type?: "
|
|
107
|
+
content_type?: "project-info" | "template" | "faq" | "guide" | "decision" | undefined;
|
|
108
108
|
customer?: string | undefined;
|
|
109
109
|
category?: "archived" | "active" | "maintenance" | "legacy" | undefined;
|
|
110
110
|
space_key?: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-current-date.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/get-current-date.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"get-current-date.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/get-current-date.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wBAAgB,wBAAwB,uJAevC"}
|
|
@@ -3,11 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
export function createGetCurrentDateTool() {
|
|
4
4
|
const getCurrentDateSchema = z.object({});
|
|
5
5
|
return tool(() => {
|
|
6
|
-
const
|
|
7
|
-
const year = currentDate.getFullYear();
|
|
8
|
-
const month = (currentDate.getMonth() + 1).toString().padStart(2, '0');
|
|
9
|
-
const day = currentDate.getDate().toString().padStart(2, '0');
|
|
10
|
-
const formattedDate = `${year}-${month}-${day}`;
|
|
6
|
+
const formattedDate = new Date().toISOString().slice(0, 10);
|
|
11
7
|
console.log(`\nš
Returning current date: ${formattedDate}`);
|
|
12
8
|
return formattedDate;
|
|
13
9
|
}, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubblecommerce/overmind-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared infrastructure package for the Overmind AI agent system",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -67,6 +67,9 @@
|
|
|
67
67
|
"zod": "^3.25.76"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
|
+
"test": "vitest run",
|
|
71
|
+
"test:watch": "vitest",
|
|
72
|
+
"test:coverage": "vitest run --coverage",
|
|
70
73
|
"build": "tsc",
|
|
71
74
|
"prepack": "npm run build",
|
|
72
75
|
"typecheck": "tsc --noEmit",
|
|
@@ -84,12 +87,14 @@
|
|
|
84
87
|
"@types/node": "^24.6.1",
|
|
85
88
|
"@types/pg": "^8.15.5",
|
|
86
89
|
"@types/turndown": "^5.0.5",
|
|
90
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
87
91
|
"eslint": "^9.36.0",
|
|
88
92
|
"husky": "^9.0.0",
|
|
89
93
|
"lint-staged": "^15.0.0",
|
|
90
94
|
"tsx": "^4.20.6",
|
|
91
95
|
"typescript": "^5.9.3",
|
|
92
|
-
"typescript-eslint": "^8.45.0"
|
|
96
|
+
"typescript-eslint": "^8.45.0",
|
|
97
|
+
"vitest": "^4.1.2"
|
|
93
98
|
},
|
|
94
99
|
"lint-staged": {
|
|
95
100
|
"*.ts": "eslint --fix"
|