@specpow/framework 0.5.28 → 0.5.29

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 (46) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +41 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/archive.d.ts.map +1 -1
  5. package/dist/commands/archive.js +46 -0
  6. package/dist/commands/archive.js.map +1 -1
  7. package/dist/commands/memory.d.ts +20 -0
  8. package/dist/commands/memory.d.ts.map +1 -1
  9. package/dist/commands/memory.js +267 -0
  10. package/dist/commands/memory.js.map +1 -1
  11. package/dist/core/artifact-graph/types.d.ts +4 -4
  12. package/dist/core/config/config-schema.d.ts +3097 -0
  13. package/dist/core/config/config-schema.d.ts.map +1 -1
  14. package/dist/core/config/config-schema.js +77 -0
  15. package/dist/core/config/config-schema.js.map +1 -1
  16. package/dist/core/init/engine.d.ts +4 -0
  17. package/dist/core/init/engine.d.ts.map +1 -1
  18. package/dist/core/init/engine.js +42 -0
  19. package/dist/core/init/engine.js.map +1 -1
  20. package/dist/core/memory/adapters/auto-collector.d.ts +59 -0
  21. package/dist/core/memory/adapters/auto-collector.d.ts.map +1 -0
  22. package/dist/core/memory/adapters/auto-collector.js +126 -0
  23. package/dist/core/memory/adapters/auto-collector.js.map +1 -0
  24. package/dist/core/memory/adapters/claude-code-adapter.d.ts +51 -0
  25. package/dist/core/memory/adapters/claude-code-adapter.d.ts.map +1 -0
  26. package/dist/core/memory/adapters/claude-code-adapter.js +197 -0
  27. package/dist/core/memory/adapters/claude-code-adapter.js.map +1 -0
  28. package/dist/core/memory/adapters/codebuddy-adapter.d.ts +44 -0
  29. package/dist/core/memory/adapters/codebuddy-adapter.d.ts.map +1 -0
  30. package/dist/core/memory/adapters/codebuddy-adapter.js +194 -0
  31. package/dist/core/memory/adapters/codebuddy-adapter.js.map +1 -0
  32. package/dist/core/memory/adapters/index.d.ts +56 -0
  33. package/dist/core/memory/adapters/index.d.ts.map +1 -0
  34. package/dist/core/memory/adapters/index.js +184 -0
  35. package/dist/core/memory/adapters/index.js.map +1 -0
  36. package/dist/core/memory/memory-injector.d.ts +101 -0
  37. package/dist/core/memory/memory-injector.d.ts.map +1 -0
  38. package/dist/core/memory/memory-injector.js +269 -0
  39. package/dist/core/memory/memory-injector.js.map +1 -0
  40. package/dist/core/memory/memory-types.d.ts +79 -0
  41. package/dist/core/memory/memory-types.d.ts.map +1 -1
  42. package/dist/core/memory/templates/phase-summary-renderer.d.ts +34 -0
  43. package/dist/core/memory/templates/phase-summary-renderer.d.ts.map +1 -0
  44. package/dist/core/memory/templates/phase-summary-renderer.js +72 -0
  45. package/dist/core/memory/templates/phase-summary-renderer.js.map +1 -0
  46. package/package.json +1 -1
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Claude Code 会话适配器
3
+ *
4
+ * 从 Claude Code 的会话日志中读取消息并转换为标准化的 SessionLog 格式
5
+ *
6
+ * 存储结构:
7
+ * - Windows: C:\Users\{user}\.claude\projects\{encoded-path}\{sessionId}.jsonl
8
+ * - macOS/Linux: ~/.claude/projects/{encoded-path}/{sessionId}.jsonl
9
+ *
10
+ * 项目路径编码规则:将 : 和 / 替换为 -
11
+ */
12
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
13
+ import { homedir } from 'os';
14
+ import { join } from 'path';
15
+ /**
16
+ * Claude Code 会话适配器
17
+ */
18
+ export class ClaudeCodeAdapter {
19
+ name = 'claudeCode';
20
+ baseDir;
21
+ projectsDir;
22
+ constructor() {
23
+ const home = homedir();
24
+ this.baseDir = join(home, '.claude');
25
+ this.projectsDir = join(this.baseDir, 'projects');
26
+ }
27
+ /**
28
+ * 检测 Claude Code 是否已安装
29
+ */
30
+ async detect() {
31
+ return existsSync(this.projectsDir);
32
+ }
33
+ /**
34
+ * 列出所有项目
35
+ */
36
+ async listProjects() {
37
+ if (!existsSync(this.projectsDir)) {
38
+ return [];
39
+ }
40
+ const entries = readdirSync(this.projectsDir);
41
+ const projects = [];
42
+ for (const entry of entries) {
43
+ const entryPath = join(this.projectsDir, entry);
44
+ const stat = statSync(entryPath);
45
+ if (stat.isDirectory()) {
46
+ projects.push(entry);
47
+ }
48
+ }
49
+ return projects;
50
+ }
51
+ /**
52
+ * 列出指定项目的所有会话
53
+ */
54
+ async listSessions(project) {
55
+ const projectDir = join(this.projectsDir, project);
56
+ if (!existsSync(projectDir)) {
57
+ return [];
58
+ }
59
+ const files = readdirSync(projectDir);
60
+ const sessions = [];
61
+ for (const file of files) {
62
+ if (!file.endsWith('.jsonl')) {
63
+ continue;
64
+ }
65
+ const sessionId = file.replace('.jsonl', '');
66
+ const filePath = join(projectDir, file);
67
+ try {
68
+ const meta = this.getSessionMeta(filePath, sessionId);
69
+ if (meta) {
70
+ sessions.push(meta);
71
+ }
72
+ }
73
+ catch {
74
+ // 跳过无法解析的文件
75
+ }
76
+ }
77
+ return sessions;
78
+ }
79
+ /**
80
+ * 加载指定会话并转换为标准格式
81
+ */
82
+ async loadSession(project, sessionId) {
83
+ const filePath = join(this.projectsDir, project, `${sessionId}.jsonl`);
84
+ if (!existsSync(filePath)) {
85
+ throw new Error(`Session file not found: ${filePath}`);
86
+ }
87
+ const content = readFileSync(filePath, 'utf-8');
88
+ const lines = content.split('\n').filter(line => line.trim());
89
+ const messages = [];
90
+ let startTime = Date.now();
91
+ let endTime;
92
+ for (const line of lines) {
93
+ try {
94
+ const msg = JSON.parse(line);
95
+ // 只处理 user 和 assistant 消息
96
+ if (msg.type !== 'user' && msg.type !== 'assistant') {
97
+ continue;
98
+ }
99
+ // 提取内容
100
+ let content = '';
101
+ if (msg.message?.content) {
102
+ if (typeof msg.message.content === 'string') {
103
+ content = msg.message.content;
104
+ }
105
+ else if (Array.isArray(msg.message.content)) {
106
+ content = msg.message.content
107
+ .filter(block => block.type === 'text' && block.text)
108
+ .map(block => block.text)
109
+ .join('\n');
110
+ }
111
+ }
112
+ // 跳过过短的消息
113
+ if (content.length < 20) {
114
+ continue;
115
+ }
116
+ const timestamp = msg.timestamp ? new Date(msg.timestamp).getTime() : Date.now();
117
+ if (timestamp < startTime) {
118
+ startTime = timestamp;
119
+ }
120
+ if (!endTime || timestamp > endTime) {
121
+ endTime = timestamp;
122
+ }
123
+ messages.push({
124
+ timestamp,
125
+ type: msg.type,
126
+ content,
127
+ metadata: {
128
+ uuid: msg.uuid,
129
+ cwd: msg.cwd,
130
+ },
131
+ });
132
+ }
133
+ catch {
134
+ // 跳过无法解析的行
135
+ }
136
+ }
137
+ return {
138
+ sessionId,
139
+ startTime,
140
+ endTime,
141
+ messages,
142
+ };
143
+ }
144
+ /**
145
+ * 获取会话元数据
146
+ */
147
+ getSessionMeta(filePath, sessionId) {
148
+ const content = readFileSync(filePath, 'utf-8');
149
+ const lines = content.split('\n').filter(line => line.trim());
150
+ if (lines.length === 0) {
151
+ return null;
152
+ }
153
+ let createdAt = Date.now();
154
+ let lastMessageAt;
155
+ let messageCount = 0;
156
+ for (const line of lines) {
157
+ try {
158
+ const msg = JSON.parse(line);
159
+ if (msg.type === 'user' || msg.type === 'assistant') {
160
+ messageCount++;
161
+ const timestamp = msg.timestamp ? new Date(msg.timestamp).getTime() : Date.now();
162
+ if (timestamp < createdAt) {
163
+ createdAt = timestamp;
164
+ }
165
+ if (!lastMessageAt || timestamp > lastMessageAt) {
166
+ lastMessageAt = timestamp;
167
+ }
168
+ }
169
+ }
170
+ catch {
171
+ // 跳过
172
+ }
173
+ }
174
+ return {
175
+ id: sessionId,
176
+ createdAt,
177
+ lastMessageAt,
178
+ messageCount,
179
+ };
180
+ }
181
+ }
182
+ /**
183
+ * 编码项目路径
184
+ * 将 : 和 / 替换为 -
185
+ */
186
+ export function encodeProjectPath(projectPath) {
187
+ return projectPath.replace(/[:/\\]/g, '-');
188
+ }
189
+ /**
190
+ * 解码项目路径
191
+ */
192
+ export function decodeProjectPath(encoded) {
193
+ // 注意:这个解码不完美,因为原始路径可能包含 -
194
+ // 但在大多数情况下足够使用
195
+ return encoded.replace(/^-/, '').replace(/-/g, '/');
196
+ }
197
+ //# sourceMappingURL=claude-code-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-adapter.js","sourceRoot":"","sources":["../../../../src/core/memory/adapters/claude-code-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * CodeBuddy 会话适配器
3
+ *
4
+ * 从 CodeBuddy 的会话存储中读取消息并转换为标准化的 SessionLog 格式
5
+ *
6
+ * 存储结构(Windows):
7
+ * %LOCALAPPDATA%\CodeBuddyExtension\Data\{userId}\JetBrains\{userId}\history\{projectHash}\
8
+ * ├── index.json # 会话列表
9
+ * └── {conversationId}\
10
+ * ├── index.json # 消息索引
11
+ * └── messages\
12
+ * └── {messageId}.json # 单条消息
13
+ */
14
+ import type { SessionAdapter, SessionMeta, SessionLog } from '../memory-types.js';
15
+ /**
16
+ * CodeBuddy 会话适配器
17
+ */
18
+ export declare class CodeBuddyAdapter implements SessionAdapter {
19
+ name: string;
20
+ private dataDir;
21
+ private historyDir;
22
+ constructor();
23
+ /**
24
+ * 检测 CodeBuddy 是否已安装
25
+ */
26
+ detect(): Promise<boolean>;
27
+ /**
28
+ * 列出所有项目(projectHash)
29
+ */
30
+ listProjects(): Promise<string[]>;
31
+ /**
32
+ * 列出指定项目的所有会话
33
+ */
34
+ listSessions(project: string): Promise<SessionMeta[]>;
35
+ /**
36
+ * 加载指定会话并转换为标准格式
37
+ */
38
+ loadSession(project: string, sessionId: string): Promise<SessionLog>;
39
+ }
40
+ /**
41
+ * 计算项目路径的 MD5 哈希
42
+ */
43
+ export declare function computeProjectHash(projectPath: string): string;
44
+ //# sourceMappingURL=codebuddy-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebuddy-adapter.d.ts","sourceRoot":"","sources":["../../../../src/core/memory/adapters/codebuddy-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAoDlG;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,IAAI,SAAe;IAEnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAuB;;IAOzC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAqBhC;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAsBvC;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA+B3D;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAmG3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * CodeBuddy 会话适配器
3
+ *
4
+ * 从 CodeBuddy 的会话存储中读取消息并转换为标准化的 SessionLog 格式
5
+ *
6
+ * 存储结构(Windows):
7
+ * %LOCALAPPDATA%\CodeBuddyExtension\Data\{userId}\JetBrains\{userId}\history\{projectHash}\
8
+ * ├── index.json # 会话列表
9
+ * └── {conversationId}\
10
+ * ├── index.json # 消息索引
11
+ * └── messages\
12
+ * └── {messageId}.json # 单条消息
13
+ */
14
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { createHash } from 'crypto';
17
+ /**
18
+ * CodeBuddy 会话适配器
19
+ */
20
+ export class CodeBuddyAdapter {
21
+ name = 'codebuddy';
22
+ dataDir;
23
+ historyDir = null;
24
+ constructor() {
25
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA?.replace('Roaming', 'Local') || '';
26
+ this.dataDir = join(localAppData, 'CodeBuddyExtension', 'Data');
27
+ }
28
+ /**
29
+ * 检测 CodeBuddy 是否已安装
30
+ */
31
+ async detect() {
32
+ if (!existsSync(this.dataDir)) {
33
+ return false;
34
+ }
35
+ // 查找用户目录
36
+ const entries = readdirSync(this.dataDir);
37
+ for (const entry of entries) {
38
+ const entryPath = join(this.dataDir, entry);
39
+ if (statSync(entryPath).isDirectory() && entry !== 'default' && entry !== 'Public') {
40
+ const jetbrainsDir = join(entryPath, 'JetBrains', entry, 'history');
41
+ if (existsSync(jetbrainsDir)) {
42
+ this.historyDir = jetbrainsDir;
43
+ return true;
44
+ }
45
+ }
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * 列出所有项目(projectHash)
51
+ */
52
+ async listProjects() {
53
+ if (!this.historyDir || !existsSync(this.historyDir)) {
54
+ await this.detect();
55
+ }
56
+ if (!this.historyDir) {
57
+ return [];
58
+ }
59
+ const entries = readdirSync(this.historyDir);
60
+ const projects = [];
61
+ for (const entry of entries) {
62
+ const entryPath = join(this.historyDir, entry);
63
+ if (statSync(entryPath).isDirectory()) {
64
+ projects.push(entry);
65
+ }
66
+ }
67
+ return projects;
68
+ }
69
+ /**
70
+ * 列出指定项目的所有会话
71
+ */
72
+ async listSessions(project) {
73
+ if (!this.historyDir) {
74
+ await this.detect();
75
+ }
76
+ if (!this.historyDir) {
77
+ return [];
78
+ }
79
+ const projectDir = join(this.historyDir, project);
80
+ const indexPath = join(projectDir, 'index.json');
81
+ if (!existsSync(indexPath)) {
82
+ return [];
83
+ }
84
+ try {
85
+ const content = readFileSync(indexPath, 'utf-8');
86
+ const index = JSON.parse(content);
87
+ return index.conversations.map(conv => ({
88
+ id: conv.id,
89
+ name: conv.name,
90
+ createdAt: new Date(conv.createdAt).getTime(),
91
+ lastMessageAt: new Date(conv.lastMessageAt).getTime(),
92
+ }));
93
+ }
94
+ catch {
95
+ return [];
96
+ }
97
+ }
98
+ /**
99
+ * 加载指定会话并转换为标准格式
100
+ */
101
+ async loadSession(project, sessionId) {
102
+ if (!this.historyDir) {
103
+ await this.detect();
104
+ }
105
+ if (!this.historyDir) {
106
+ throw new Error('CodeBuddy history directory not found');
107
+ }
108
+ const conversationDir = join(this.historyDir, project, sessionId);
109
+ const messagesDir = join(conversationDir, 'messages');
110
+ const messageIndexPath = join(conversationDir, 'index.json');
111
+ if (!existsSync(messageIndexPath)) {
112
+ throw new Error(`Conversation index not found: ${messageIndexPath}`);
113
+ }
114
+ // 读取消息索引
115
+ const indexContent = readFileSync(messageIndexPath, 'utf-8');
116
+ const messageIndex = JSON.parse(indexContent);
117
+ const messages = [];
118
+ let startTime = Date.now();
119
+ let endTime;
120
+ // 遍历消息列表
121
+ for (const msgRef of messageIndex.messages) {
122
+ const msgPath = join(messagesDir, `${msgRef.id}.json`);
123
+ if (!existsSync(msgPath)) {
124
+ continue;
125
+ }
126
+ try {
127
+ const msgContent = readFileSync(msgPath, 'utf-8');
128
+ const msg = JSON.parse(msgContent);
129
+ // 只处理 user 和 assistant 消息
130
+ if (msg.role !== 'user' && msg.role !== 'assistant') {
131
+ continue;
132
+ }
133
+ // 解析 message 字段(JSON 字符串)
134
+ let content = '';
135
+ try {
136
+ const messageData = JSON.parse(msg.message);
137
+ if (messageData.content) {
138
+ if (typeof messageData.content === 'string') {
139
+ content = messageData.content;
140
+ }
141
+ else if (Array.isArray(messageData.content)) {
142
+ content = messageData.content
143
+ .filter((block) => block.type === 'text' && block.text)
144
+ .map((block) => block.text)
145
+ .join('\n');
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // 如果解析失败,使用原始 message 字段
151
+ content = msg.message;
152
+ }
153
+ // 跳过过短的消息
154
+ if (content.length < 20) {
155
+ continue;
156
+ }
157
+ const timestamp = new Date(msg.createdAt).getTime();
158
+ if (timestamp < startTime) {
159
+ startTime = timestamp;
160
+ }
161
+ if (!endTime || timestamp > endTime) {
162
+ endTime = timestamp;
163
+ }
164
+ messages.push({
165
+ timestamp,
166
+ type: msg.role,
167
+ content,
168
+ metadata: {
169
+ id: msg.id,
170
+ extra: msg.extra,
171
+ },
172
+ });
173
+ }
174
+ catch {
175
+ // 跳过无法解析的消息
176
+ }
177
+ }
178
+ // 按时间排序
179
+ messages.sort((a, b) => a.timestamp - b.timestamp);
180
+ return {
181
+ sessionId,
182
+ startTime,
183
+ endTime,
184
+ messages,
185
+ };
186
+ }
187
+ }
188
+ /**
189
+ * 计算项目路径的 MD5 哈希
190
+ */
191
+ export function computeProjectHash(projectPath) {
192
+ return createHash('md5').update(projectPath.toLowerCase()).digest('hex');
193
+ }
194
+ //# sourceMappingURL=codebuddy-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebuddy-adapter.js","sourceRoot":"","sources":["../../../../src/core/memory/adapters/codebuddy-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 适配器注册表
3
+ *
4
+ * 统一管理所有会话适配器,提供数据源检测、会话索引等功能
5
+ */
6
+ import type { SessionAdapter, AdapterRegistry, SourceDetection, IndexOptions, IndexResult, MemoryManager } from '../memory-types.js';
7
+ /**
8
+ * 适配器注册表实现
9
+ */
10
+ export declare class AdapterRegistryImpl implements AdapterRegistry {
11
+ private adapters;
12
+ private memoryManager;
13
+ private projectRoot;
14
+ private indexedSessionsPath;
15
+ constructor(projectRoot: string, memoryManager?: MemoryManager);
16
+ /**
17
+ * 注册适配器
18
+ */
19
+ register(adapter: SessionAdapter): void;
20
+ /**
21
+ * 检测所有数据源
22
+ */
23
+ detectSources(): Promise<SourceDetection[]>;
24
+ /**
25
+ * 获取指定适配器
26
+ */
27
+ getAdapter(name: string): SessionAdapter | undefined;
28
+ /**
29
+ * 索引所有数据源
30
+ */
31
+ indexAll(options?: IndexOptions): Promise<IndexResult>;
32
+ /**
33
+ * 设置 Memory 管理器
34
+ */
35
+ setMemoryManager(manager: MemoryManager): void;
36
+ /**
37
+ * 清除索引缓存
38
+ */
39
+ clearIndexCache(): void;
40
+ /**
41
+ * 加载已索引记录
42
+ */
43
+ private loadIndexedSessions;
44
+ /**
45
+ * 保存已索引记录
46
+ */
47
+ private saveIndexedSessions;
48
+ }
49
+ /**
50
+ * 创建适配器注册表
51
+ * 自动注册 Claude Code 和 CodeBuddy 适配器
52
+ */
53
+ export declare function createAdapterRegistry(projectRoot: string, memoryManager?: MemoryManager): AdapterRegistryImpl;
54
+ export { ClaudeCodeAdapter } from './claude-code-adapter.js';
55
+ export { CodeBuddyAdapter } from './codebuddy-adapter.js';
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/memory/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,oBAAoB,CAAC;AAW5B;;GAEG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAS;gBAExB,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,aAAa;IAM9D;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIvC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAsCjD;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIpD;;OAEG;IACG,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IAmE5D;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAI9C;;OAEG;IACH,eAAe,IAAI,IAAI;IAKvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAQ5B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,aAAa,GAC5B,mBAAmB,CAQrB;AAGD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}