@mrpatronz/nexusflow 1.3.2 → 1.5.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.
- package/README.md +50 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +54 -37
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +16 -73
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +58 -318
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/finish.d.ts +17 -0
- package/dist/commands/finish.d.ts.map +1 -0
- package/dist/commands/finish.js +172 -0
- package/dist/commands/finish.js.map +1 -0
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +11 -6
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/knowledge.d.ts +36 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +184 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/open.d.ts.map +1 -1
- package/dist/commands/open.js +2 -4
- package/dist/commands/open.js.map +1 -1
- package/dist/core/commit.d.ts +41 -0
- package/dist/core/commit.d.ts.map +1 -0
- package/dist/core/commit.js +53 -0
- package/dist/core/commit.js.map +1 -0
- package/dist/core/config.d.ts +3 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +60 -32
- package/dist/core/config.js.map +1 -1
- package/dist/core/config.test.js +24 -0
- package/dist/core/config.test.js.map +1 -1
- package/dist/core/diff.d.ts +27 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +50 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/doctor.d.ts +34 -0
- package/dist/core/doctor.d.ts.map +1 -0
- package/dist/core/doctor.js +276 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/finish.d.ts +56 -0
- package/dist/core/finish.d.ts.map +1 -0
- package/dist/core/finish.js +121 -0
- package/dist/core/finish.js.map +1 -0
- package/dist/core/finish.test.d.ts +2 -0
- package/dist/core/finish.test.d.ts.map +1 -0
- package/dist/core/finish.test.js +110 -0
- package/dist/core/finish.test.js.map +1 -0
- package/dist/core/knowledge.d.ts +96 -0
- package/dist/core/knowledge.d.ts.map +1 -0
- package/dist/core/knowledge.js +299 -0
- package/dist/core/knowledge.js.map +1 -0
- package/dist/core/knowledge.test.d.ts +2 -0
- package/dist/core/knowledge.test.d.ts.map +1 -0
- package/dist/core/knowledge.test.js +157 -0
- package/dist/core/knowledge.test.js.map +1 -0
- package/dist/core/refresh.test.d.ts +2 -0
- package/dist/core/refresh.test.d.ts.map +1 -0
- package/dist/core/refresh.test.js +67 -0
- package/dist/core/refresh.test.js.map +1 -0
- package/dist/core/status.d.ts +45 -0
- package/dist/core/status.d.ts.map +1 -0
- package/dist/core/status.js +45 -0
- package/dist/core/status.js.map +1 -0
- package/dist/core/sync.test.d.ts +2 -0
- package/dist/core/sync.test.d.ts.map +1 -0
- package/dist/core/sync.test.js +86 -0
- package/dist/core/sync.test.js.map +1 -0
- package/dist/core/workspace.create.test.d.ts +2 -0
- package/dist/core/workspace.create.test.d.ts.map +1 -0
- package/dist/core/workspace.create.test.js +95 -0
- package/dist/core/workspace.create.test.js.map +1 -0
- package/dist/core/workspace.d.ts +9 -3
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +91 -13
- package/dist/core/workspace.js.map +1 -1
- package/dist/core/worktree.d.ts +11 -1
- package/dist/core/worktree.d.ts.map +1 -1
- package/dist/core/worktree.js +2 -0
- package/dist/core/worktree.js.map +1 -1
- package/dist/core/worktree.test.d.ts +2 -0
- package/dist/core/worktree.test.d.ts.map +1 -0
- package/dist/core/worktree.test.js +111 -0
- package/dist/core/worktree.test.js.map +1 -0
- package/dist/generators/base.js +1 -1
- package/dist/generators/index.d.ts +7 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +4 -1
- package/dist/generators/index.js.map +1 -1
- package/dist/index.js +110 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +51 -282
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +45 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +413 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +43 -26
- package/dist/server.js.map +1 -1
- package/dist/utils/debug.d.ts +17 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +25 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/detect-ai.d.ts +14 -6
- package/dist/utils/detect-ai.d.ts.map +1 -1
- package/dist/utils/detect-ai.js +22 -9
- package/dist/utils/detect-ai.js.map +1 -1
- package/dist/utils/detect-ai.test.js +33 -0
- package/dist/utils/detect-ai.test.js.map +1 -1
- package/dist/utils/git.d.ts +13 -2
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +64 -5
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/git.test.js +46 -8
- package/dist/utils/git.test.js.map +1 -1
- package/dist/utils/multi-git.d.ts +40 -0
- package/dist/utils/multi-git.d.ts.map +1 -1
- package/dist/utils/multi-git.js +80 -0
- package/dist/utils/multi-git.js.map +1 -1
- package/dist/utils/multi-git.test.js +54 -1
- package/dist/utils/multi-git.test.js.map +1 -1
- package/dist/utils/open-editor.d.ts +20 -0
- package/dist/utils/open-editor.d.ts.map +1 -0
- package/dist/utils/open-editor.js +44 -0
- package/dist/utils/open-editor.js.map +1 -0
- package/dist/utils/pr.d.ts +50 -0
- package/dist/utils/pr.d.ts.map +1 -0
- package/dist/utils/pr.js +144 -0
- package/dist/utils/pr.js.map +1 -0
- package/dist/utils/pr.test.d.ts +2 -0
- package/dist/utils/pr.test.d.ts.map +1 -0
- package/dist/utils/pr.test.js +47 -0
- package/dist/utils/pr.test.js.map +1 -0
- package/dist/utils/resolve-workspace.d.ts +30 -0
- package/dist/utils/resolve-workspace.d.ts.map +1 -0
- package/dist/utils/resolve-workspace.js +77 -0
- package/dist/utils/resolve-workspace.js.map +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/knowledge
|
|
3
|
+
* Single engine for capturing workspace/base knowledge, shared by the CLI
|
|
4
|
+
* `knowledge` command, the dashboard server, and the MCP `add_knowledge` /
|
|
5
|
+
* `promote_knowledge` tools.
|
|
6
|
+
*
|
|
7
|
+
* All file I/O routes through the active storage adapter (`core/storage.ts`)
|
|
8
|
+
* so the local, central-vault, and Obsidian backends keep working — the
|
|
9
|
+
* previous direct-`fs` knowledge routes silently wrote to a file the
|
|
10
|
+
* generators never read under the Obsidian adapter.
|
|
11
|
+
*
|
|
12
|
+
* The markdown helpers (`insertUnderHeading`, `formatEntry`,
|
|
13
|
+
* `parseKnowledgeEntries`) are pure and section-aware: they insert under the
|
|
14
|
+
* correct heading, tolerate leading YAML frontmatter (Obsidian) and CRLF, and
|
|
15
|
+
* never destroy existing content.
|
|
16
|
+
*/
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import { loadFeatureConfig } from './workspace.js';
|
|
19
|
+
import { readWorkspaceFile, writeWorkspaceFile, workspaceFileExists, resolveWorkspaceFileUrl, readBaseFile, writeBaseFile, baseFileExists, resolveBaseFileUrl, } from './storage.js';
|
|
20
|
+
import { buildBaseKnowledgeContent } from '../generators/index.js';
|
|
21
|
+
/** The knowledge filename used for both the workspace and base layers. */
|
|
22
|
+
const KNOWLEDGE_FILE = 'nexusflow-knowledge.md';
|
|
23
|
+
/**
|
|
24
|
+
* Heading aliases per entry type for the workspace and base knowledge files.
|
|
25
|
+
* The first alias is the canonical heading used when the section is missing.
|
|
26
|
+
* `base: null` means the type is not promotable to base knowledge.
|
|
27
|
+
*/
|
|
28
|
+
const SECTION_ALIASES = {
|
|
29
|
+
decision: {
|
|
30
|
+
workspace: ['Architecture Decisions'],
|
|
31
|
+
base: ['Architecture Decisions'],
|
|
32
|
+
},
|
|
33
|
+
gotcha: {
|
|
34
|
+
workspace: ['Known Gotchas', 'Discovered Gotchas & Watch-outs', 'Discovered Gotchas'],
|
|
35
|
+
base: ['Discovered Gotchas & Watch-outs', 'Known Gotchas', 'Discovered Gotchas'],
|
|
36
|
+
},
|
|
37
|
+
progress: {
|
|
38
|
+
workspace: ['Implementation Progress'],
|
|
39
|
+
base: null,
|
|
40
|
+
},
|
|
41
|
+
assumption: {
|
|
42
|
+
workspace: ['Project Assumptions (verify with user)', 'Project Assumptions'],
|
|
43
|
+
base: ['Coding Conventions & Invariants'],
|
|
44
|
+
},
|
|
45
|
+
question: {
|
|
46
|
+
workspace: ['Clarifying Questions for the User', 'Clarifying Questions'],
|
|
47
|
+
base: null,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
// ─── Pure markdown helpers ──────────────────────────────────────────────────
|
|
51
|
+
/** True for template placeholder lines that should be replaced by real content. */
|
|
52
|
+
function isPlaceholder(line) {
|
|
53
|
+
const t = line.trim();
|
|
54
|
+
if (!t)
|
|
55
|
+
return false;
|
|
56
|
+
if (/^_\(.*\)_$/.test(t))
|
|
57
|
+
return true; // e.g. _(No gotchas recorded yet.)_
|
|
58
|
+
if (/^-\s*None recorded yet\.?$/i.test(t))
|
|
59
|
+
return true;
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
function todayIso() {
|
|
63
|
+
return new Date().toISOString().slice(0, 10);
|
|
64
|
+
}
|
|
65
|
+
function truncateHeading(text, max = 60) {
|
|
66
|
+
const oneLine = text.replace(/\s+/g, ' ').trim();
|
|
67
|
+
return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max).trimEnd()}…`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Formats a {@link KnowledgeEntry} as the markdown snippet to insert.
|
|
71
|
+
*
|
|
72
|
+
* @param entry - The entry to format.
|
|
73
|
+
* @param _target - Reserved for target-specific formatting (currently identical).
|
|
74
|
+
*/
|
|
75
|
+
export function formatEntry(entry, _target) {
|
|
76
|
+
const date = (entry.timestamp ?? new Date().toISOString()).slice(0, 10);
|
|
77
|
+
const msg = entry.message.trim();
|
|
78
|
+
switch (entry.type) {
|
|
79
|
+
case 'decision': {
|
|
80
|
+
const heading = entry.title?.trim() ? entry.title.trim() : truncateHeading(msg);
|
|
81
|
+
return `### ${date} — ${heading}\n**Decision:** ${msg}`;
|
|
82
|
+
}
|
|
83
|
+
case 'progress':
|
|
84
|
+
return `- [x] ${date} — ${msg}`;
|
|
85
|
+
case 'gotcha':
|
|
86
|
+
case 'assumption':
|
|
87
|
+
case 'question':
|
|
88
|
+
default:
|
|
89
|
+
return `- **${date}:** ${msg}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Inserts `entryMarkdown` under the first `## ` heading matching any of
|
|
94
|
+
* `headingAliases` (case-insensitive), stripping placeholder lines. When no
|
|
95
|
+
* matching heading exists, a new section is appended at end-of-file using the
|
|
96
|
+
* first alias. Existing content is never removed. CRLF line endings and a
|
|
97
|
+
* leading YAML frontmatter block are preserved.
|
|
98
|
+
*
|
|
99
|
+
* @returns The updated markdown.
|
|
100
|
+
*/
|
|
101
|
+
export function insertUnderHeading(markdown, headingAliases, entryMarkdown) {
|
|
102
|
+
const eol = markdown.includes('\r\n') ? '\r\n' : '\n';
|
|
103
|
+
const lines = markdown.split(/\r?\n/);
|
|
104
|
+
const aliasSet = new Set(headingAliases.map((a) => a.trim().toLowerCase()));
|
|
105
|
+
// Find the target section heading.
|
|
106
|
+
let headingIdx = -1;
|
|
107
|
+
for (let i = 0; i < lines.length; i++) {
|
|
108
|
+
const m = lines[i].match(/^##\s+(.*)$/);
|
|
109
|
+
if (m && aliasSet.has(m[1].trim().toLowerCase())) {
|
|
110
|
+
headingIdx = i;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const entryLines = entryMarkdown.split(/\r?\n/);
|
|
115
|
+
if (headingIdx === -1) {
|
|
116
|
+
// Append a fresh section at end-of-file.
|
|
117
|
+
const trimmed = lines.join(eol).replace(/\s+$/, '');
|
|
118
|
+
const prefix = trimmed.length > 0 ? `${trimmed}${eol}${eol}` : '';
|
|
119
|
+
return `${prefix}## ${headingAliases[0]}${eol}${eol}${entryLines.join(eol)}${eol}`;
|
|
120
|
+
}
|
|
121
|
+
// Determine the section body range: until the next h1/h2 or EOF.
|
|
122
|
+
let sectionEnd = lines.length;
|
|
123
|
+
for (let i = headingIdx + 1; i < lines.length; i++) {
|
|
124
|
+
if (/^#{1,2}\s/.test(lines[i])) {
|
|
125
|
+
sectionEnd = i;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const before = lines.slice(0, headingIdx + 1);
|
|
130
|
+
const after = lines.slice(sectionEnd);
|
|
131
|
+
// Keep comments and real content; drop placeholders and trailing blanks.
|
|
132
|
+
const body = lines.slice(headingIdx + 1, sectionEnd).filter((l) => !isPlaceholder(l));
|
|
133
|
+
while (body.length > 0 && body[body.length - 1].trim() === '') {
|
|
134
|
+
body.pop();
|
|
135
|
+
}
|
|
136
|
+
// One blank line between the heading (or existing content) and the new entry.
|
|
137
|
+
const newSection = [...before, ...body, '', ...entryLines, ''];
|
|
138
|
+
// Preserve a single blank line before the next section, if any.
|
|
139
|
+
const tail = after.length > 0 ? [...after] : [];
|
|
140
|
+
return [...newSection, ...tail].join(eol);
|
|
141
|
+
}
|
|
142
|
+
/** Reverse lookup: which entry type owns a given `## ` heading, if any. */
|
|
143
|
+
function sectionType(heading) {
|
|
144
|
+
const h = heading.trim().toLowerCase();
|
|
145
|
+
for (const [type, aliases] of Object.entries(SECTION_ALIASES)) {
|
|
146
|
+
const all = [...aliases.workspace, ...(aliases.base ?? [])];
|
|
147
|
+
if (all.some((a) => a.toLowerCase() === h)) {
|
|
148
|
+
return type;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Parses a knowledge markdown document into individual entries: bullet lines
|
|
155
|
+
* and `### ` decision blocks, each tagged with its section and inferred type.
|
|
156
|
+
* Placeholder and comment lines are ignored.
|
|
157
|
+
*/
|
|
158
|
+
export function parseKnowledgeEntries(markdown) {
|
|
159
|
+
const lines = markdown.split(/\r?\n/);
|
|
160
|
+
const entries = [];
|
|
161
|
+
let currentSection = '';
|
|
162
|
+
let currentType = null;
|
|
163
|
+
let decisionBuffer = null;
|
|
164
|
+
const flushDecision = () => {
|
|
165
|
+
if (decisionBuffer) {
|
|
166
|
+
const text = decisionBuffer.join('\n').trim();
|
|
167
|
+
if (text)
|
|
168
|
+
entries.push({ section: currentSection, type: currentType, text });
|
|
169
|
+
decisionBuffer = null;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
const h2 = line.match(/^##\s+(.*)$/);
|
|
174
|
+
if (h2) {
|
|
175
|
+
flushDecision();
|
|
176
|
+
currentSection = h2[1].trim();
|
|
177
|
+
currentType = sectionType(currentSection);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (/^#\s+/.test(line)) {
|
|
181
|
+
flushDecision();
|
|
182
|
+
currentSection = '';
|
|
183
|
+
currentType = null;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (line.startsWith('### ')) {
|
|
187
|
+
flushDecision();
|
|
188
|
+
decisionBuffer = [line];
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (decisionBuffer) {
|
|
192
|
+
decisionBuffer.push(line);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const t = line.trim();
|
|
196
|
+
if (t.startsWith('- ') && !isPlaceholder(line)) {
|
|
197
|
+
entries.push({ section: currentSection, type: currentType, text: t });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
flushDecision();
|
|
201
|
+
return entries;
|
|
202
|
+
}
|
|
203
|
+
// ─── Adapter-routed I/O ─────────────────────────────────────────────────────
|
|
204
|
+
async function resolveFeatureId(workspacePath) {
|
|
205
|
+
const feature = await loadFeatureConfig(workspacePath);
|
|
206
|
+
return feature?.id ?? path.basename(workspacePath);
|
|
207
|
+
}
|
|
208
|
+
function assertMessage(message) {
|
|
209
|
+
if (!message || !message.trim()) {
|
|
210
|
+
throw new Error('Knowledge entry message cannot be empty.');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** Reads the workspace knowledge file, or `null` when it does not exist. */
|
|
214
|
+
export async function readWorkspaceKnowledge(workspacePath) {
|
|
215
|
+
const featureId = await resolveFeatureId(workspacePath);
|
|
216
|
+
if (!(await workspaceFileExists(workspacePath, featureId, KNOWLEDGE_FILE))) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
return readWorkspaceFile(workspacePath, featureId, KNOWLEDGE_FILE);
|
|
220
|
+
}
|
|
221
|
+
/** Reads a repo's base knowledge file, or `null` when it does not exist. */
|
|
222
|
+
export async function readBaseKnowledge(workspacePath, repoName) {
|
|
223
|
+
if (!(await baseFileExists(workspacePath, repoName, KNOWLEDGE_FILE))) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return readBaseFile(workspacePath, repoName, KNOWLEDGE_FILE);
|
|
227
|
+
}
|
|
228
|
+
/** Appends a formatted entry to the workspace knowledge file (creating it if missing). */
|
|
229
|
+
export async function addWorkspaceKnowledge(workspacePath, entry) {
|
|
230
|
+
assertMessage(entry.message);
|
|
231
|
+
const featureId = await resolveFeatureId(workspacePath);
|
|
232
|
+
const exists = await workspaceFileExists(workspacePath, featureId, KNOWLEDGE_FILE);
|
|
233
|
+
const content = exists
|
|
234
|
+
? await readWorkspaceFile(workspacePath, featureId, KNOWLEDGE_FILE)
|
|
235
|
+
: `# Workspace Knowledge — ${featureId}\n`;
|
|
236
|
+
const aliases = SECTION_ALIASES[entry.type].workspace;
|
|
237
|
+
const updated = insertUnderHeading(content, aliases, formatEntry(entry, 'workspace'));
|
|
238
|
+
await writeWorkspaceFile(workspacePath, featureId, KNOWLEDGE_FILE, updated);
|
|
239
|
+
return {
|
|
240
|
+
location: resolveWorkspaceFileUrl(workspacePath, featureId, KNOWLEDGE_FILE),
|
|
241
|
+
section: aliases[0],
|
|
242
|
+
createdFile: !exists,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Inserts already-formatted markdown under a repo's base knowledge section for
|
|
247
|
+
* the given type (creating the file if missing).
|
|
248
|
+
*/
|
|
249
|
+
async function insertIntoBase(workspacePath, repoName, type, entryMarkdown) {
|
|
250
|
+
const aliases = SECTION_ALIASES[type].base;
|
|
251
|
+
if (!aliases) {
|
|
252
|
+
throw new Error(`'${type}' entries cannot be stored in base (repo) knowledge.`);
|
|
253
|
+
}
|
|
254
|
+
const exists = await baseFileExists(workspacePath, repoName, KNOWLEDGE_FILE);
|
|
255
|
+
const content = exists
|
|
256
|
+
? await readBaseFile(workspacePath, repoName, KNOWLEDGE_FILE)
|
|
257
|
+
: buildBaseKnowledgeContent(repoName);
|
|
258
|
+
const updated = insertUnderHeading(content, aliases, entryMarkdown);
|
|
259
|
+
await writeBaseFile(workspacePath, repoName, KNOWLEDGE_FILE, updated);
|
|
260
|
+
return {
|
|
261
|
+
location: resolveBaseFileUrl(workspacePath, repoName, KNOWLEDGE_FILE),
|
|
262
|
+
section: aliases[0],
|
|
263
|
+
createdFile: !exists,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/** Appends a formatted entry to a repo's base knowledge file (creating it if missing). */
|
|
267
|
+
export async function addBaseKnowledge(workspacePath, repoName, entry) {
|
|
268
|
+
assertMessage(entry.message);
|
|
269
|
+
return insertIntoBase(workspacePath, repoName, entry.type, formatEntry(entry, 'base'));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Promotes selected workspace learnings into a repo's persistent base
|
|
273
|
+
* knowledge so they survive across features. Entries whose type is not
|
|
274
|
+
* promotable (progress, question) are skipped. In `move` mode the source entry
|
|
275
|
+
* in the workspace file is replaced with an auditable "promoted" note.
|
|
276
|
+
*/
|
|
277
|
+
export async function promoteKnowledge(workspacePath, options) {
|
|
278
|
+
const { repoName, entries, mode = 'copy' } = options;
|
|
279
|
+
const promotable = entries.filter((e) => e.type && SECTION_ALIASES[e.type].base);
|
|
280
|
+
let baseLocation = resolveBaseFileUrl(workspacePath, repoName, KNOWLEDGE_FILE);
|
|
281
|
+
for (const e of promotable) {
|
|
282
|
+
// Preserve the entry's original markdown rather than reformatting it.
|
|
283
|
+
const res = await insertIntoBase(workspacePath, repoName, e.type, e.text);
|
|
284
|
+
baseLocation = res.location;
|
|
285
|
+
}
|
|
286
|
+
if (mode === 'move' && promotable.length > 0) {
|
|
287
|
+
const featureId = await resolveFeatureId(workspacePath);
|
|
288
|
+
if (await workspaceFileExists(workspacePath, featureId, KNOWLEDGE_FILE)) {
|
|
289
|
+
let content = await readWorkspaceFile(workspacePath, featureId, KNOWLEDGE_FILE);
|
|
290
|
+
const date = todayIso();
|
|
291
|
+
for (const e of promotable) {
|
|
292
|
+
content = content.replace(e.text, `- Promoted to ${repoName} base knowledge on ${date}.`);
|
|
293
|
+
}
|
|
294
|
+
await writeWorkspaceFile(workspacePath, featureId, KNOWLEDGE_FILE, content);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { promotedCount: promotable.length, baseLocation };
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../../src/core/knowledge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.test.d.ts","sourceRoot":"","sources":["../../src/core/knowledge.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { insertUnderHeading, formatEntry, parseKnowledgeEntries, addWorkspaceKnowledge, addBaseKnowledge, promoteKnowledge, readWorkspaceKnowledge, } from './knowledge.js';
|
|
3
|
+
import * as storage from './storage.js';
|
|
4
|
+
import * as workspace from './workspace.js';
|
|
5
|
+
import * as generators from '../generators/index.js';
|
|
6
|
+
vi.mock('./storage.js');
|
|
7
|
+
vi.mock('./workspace.js');
|
|
8
|
+
vi.mock('../generators/index.js');
|
|
9
|
+
// ─── Pure helpers ───────────────────────────────────────────────────────────
|
|
10
|
+
describe('insertUnderHeading', () => {
|
|
11
|
+
it('inserts under an existing heading and strips the placeholder', () => {
|
|
12
|
+
const md = `## Known Gotchas\n\n<!-- keep me -->\n\n_(No gotchas recorded yet.)_\n`;
|
|
13
|
+
const out = insertUnderHeading(md, ['Known Gotchas'], '- **2026-07-04:** watch the cache');
|
|
14
|
+
expect(out).toContain('- **2026-07-04:** watch the cache');
|
|
15
|
+
expect(out).not.toContain('No gotchas recorded yet');
|
|
16
|
+
expect(out).toContain('<!-- keep me -->'); // comments are preserved
|
|
17
|
+
});
|
|
18
|
+
it('matches a heading via an alias', () => {
|
|
19
|
+
const md = `## Discovered Gotchas & Watch-outs\n\n- None recorded yet.\n`;
|
|
20
|
+
const out = insertUnderHeading(md, ['Discovered Gotchas & Watch-outs', 'Known Gotchas'], '- **2026-07-04:** x');
|
|
21
|
+
expect(out).toContain('- **2026-07-04:** x');
|
|
22
|
+
expect(out).not.toContain('None recorded yet');
|
|
23
|
+
});
|
|
24
|
+
it('appends a new section when the heading is missing', () => {
|
|
25
|
+
const md = `# Workspace Knowledge — feat\n`;
|
|
26
|
+
const out = insertUnderHeading(md, ['Architecture Decisions'], '### 2026-07-04 — Chose X\n**Decision:** because reasons');
|
|
27
|
+
expect(out).toContain('## Architecture Decisions');
|
|
28
|
+
expect(out).toContain('**Decision:** because reasons');
|
|
29
|
+
});
|
|
30
|
+
it('preserves CRLF line endings', () => {
|
|
31
|
+
const md = `## Known Gotchas\r\n\r\n_(No gotchas recorded yet.)_\r\n`;
|
|
32
|
+
const out = insertUnderHeading(md, ['Known Gotchas'], '- foo');
|
|
33
|
+
expect(out.includes('\r\n')).toBe(true);
|
|
34
|
+
// No bare LF (every \n must be preceded by \r).
|
|
35
|
+
expect(/[^\r]\n/.test(out)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it('passes through a leading YAML frontmatter block', () => {
|
|
38
|
+
const md = `---\ntags: ["x"]\ntype: workspace-context\n---\n\n## Known Gotchas\n\n_(No gotchas recorded yet.)_\n`;
|
|
39
|
+
const out = insertUnderHeading(md, ['Known Gotchas'], '- foo');
|
|
40
|
+
expect(out.startsWith('---')).toBe(true);
|
|
41
|
+
expect(out).toContain('tags: ["x"]');
|
|
42
|
+
expect(out).toContain('- foo');
|
|
43
|
+
});
|
|
44
|
+
it('does not destroy an existing entry when adding another', () => {
|
|
45
|
+
const md = `## Known Gotchas\n\n- **2026-07-01:** first\n`;
|
|
46
|
+
const out = insertUnderHeading(md, ['Known Gotchas'], '- **2026-07-04:** second');
|
|
47
|
+
expect(out).toContain('- **2026-07-01:** first');
|
|
48
|
+
expect(out).toContain('- **2026-07-04:** second');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('formatEntry', () => {
|
|
52
|
+
const ts = '2026-07-04T10:00:00.000Z';
|
|
53
|
+
it('formats a decision with a title as a dated ### block', () => {
|
|
54
|
+
const out = formatEntry({ type: 'decision', message: 'use worktrees', title: 'Worktrees', timestamp: ts }, 'workspace');
|
|
55
|
+
expect(out).toBe('### 2026-07-04 — Worktrees\n**Decision:** use worktrees');
|
|
56
|
+
});
|
|
57
|
+
it('formats a gotcha as a dated bullet', () => {
|
|
58
|
+
const out = formatEntry({ type: 'gotcha', message: 'EBUSY on Windows', timestamp: ts }, 'workspace');
|
|
59
|
+
expect(out).toBe('- **2026-07-04:** EBUSY on Windows');
|
|
60
|
+
});
|
|
61
|
+
it('formats progress as a checked item', () => {
|
|
62
|
+
const out = formatEntry({ type: 'progress', message: 'shipped rollback', timestamp: ts }, 'workspace');
|
|
63
|
+
expect(out).toBe('- [x] 2026-07-04 — shipped rollback');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('parseKnowledgeEntries', () => {
|
|
67
|
+
it('parses bullet entries and decision blocks with their types', () => {
|
|
68
|
+
const md = [
|
|
69
|
+
'## Known Gotchas',
|
|
70
|
+
'',
|
|
71
|
+
'- **2026-07-04:** watch out',
|
|
72
|
+
'',
|
|
73
|
+
'## Architecture Decisions',
|
|
74
|
+
'',
|
|
75
|
+
'### 2026-07-04 — Chose X',
|
|
76
|
+
'**Decision:** because',
|
|
77
|
+
'',
|
|
78
|
+
].join('\n');
|
|
79
|
+
const parsed = parseKnowledgeEntries(md);
|
|
80
|
+
expect(parsed).toContainEqual({
|
|
81
|
+
section: 'Known Gotchas',
|
|
82
|
+
type: 'gotcha',
|
|
83
|
+
text: '- **2026-07-04:** watch out',
|
|
84
|
+
});
|
|
85
|
+
const decision = parsed.find((p) => p.type === 'decision');
|
|
86
|
+
expect(decision?.text).toContain('### 2026-07-04 — Chose X');
|
|
87
|
+
expect(decision?.text).toContain('**Decision:** because');
|
|
88
|
+
});
|
|
89
|
+
it('ignores placeholder and comment lines', () => {
|
|
90
|
+
const md = `## Known Gotchas\n\n<!-- hint -->\n\n_(No gotchas recorded yet.)_\n`;
|
|
91
|
+
expect(parseKnowledgeEntries(md)).toHaveLength(0);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
// ─── Adapter-routed I/O (in-memory storage mock) ────────────────────────────
|
|
95
|
+
describe('knowledge I/O', () => {
|
|
96
|
+
const ws = new Map();
|
|
97
|
+
const base = new Map();
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
vi.clearAllMocks();
|
|
100
|
+
ws.clear();
|
|
101
|
+
base.clear();
|
|
102
|
+
vi.mocked(workspace.loadFeatureConfig).mockResolvedValue({ id: 'feat' });
|
|
103
|
+
vi.mocked(generators.buildBaseKnowledgeContent).mockImplementation((repo) => `# Base Codebase Knowledge — ${repo}\n\n## Discovered Gotchas & Watch-outs\n- None recorded yet.\n\n## Architecture Decisions\n- None recorded yet.\n`);
|
|
104
|
+
vi.mocked(storage.workspaceFileExists).mockImplementation(async (_w, fid, fn) => ws.has(`${fid}/${fn}`));
|
|
105
|
+
vi.mocked(storage.readWorkspaceFile).mockImplementation(async (_w, fid, fn) => ws.get(`${fid}/${fn}`));
|
|
106
|
+
vi.mocked(storage.writeWorkspaceFile).mockImplementation(async (_w, fid, fn, c) => {
|
|
107
|
+
ws.set(`${fid}/${fn}`, c);
|
|
108
|
+
});
|
|
109
|
+
vi.mocked(storage.resolveWorkspaceFileUrl).mockImplementation((_w, fid, fn) => `ws:/${fid}/${fn}`);
|
|
110
|
+
vi.mocked(storage.baseFileExists).mockImplementation(async (_w, repo, fn) => base.has(`${repo}/${fn}`));
|
|
111
|
+
vi.mocked(storage.readBaseFile).mockImplementation(async (_w, repo, fn) => base.get(`${repo}/${fn}`));
|
|
112
|
+
vi.mocked(storage.writeBaseFile).mockImplementation(async (_w, repo, fn, c) => {
|
|
113
|
+
base.set(`${repo}/${fn}`, c);
|
|
114
|
+
});
|
|
115
|
+
vi.mocked(storage.resolveBaseFileUrl).mockImplementation((_w, repo, fn) => `base:/${repo}/${fn}`);
|
|
116
|
+
});
|
|
117
|
+
it('creates the workspace knowledge file when missing and records the entry', async () => {
|
|
118
|
+
const result = await addWorkspaceKnowledge('/wsp', {
|
|
119
|
+
type: 'decision',
|
|
120
|
+
message: 'store knowledge via the adapter',
|
|
121
|
+
title: 'Adapter routing',
|
|
122
|
+
});
|
|
123
|
+
expect(result.createdFile).toBe(true);
|
|
124
|
+
const content = ws.get('feat/nexusflow-knowledge.md');
|
|
125
|
+
expect(content).toContain('## Architecture Decisions');
|
|
126
|
+
expect(content).toContain('**Decision:** store knowledge via the adapter');
|
|
127
|
+
});
|
|
128
|
+
it('rejects an empty message', async () => {
|
|
129
|
+
await expect(addWorkspaceKnowledge('/wsp', { type: 'gotcha', message: ' ' })).rejects.toThrow(/cannot be empty/i);
|
|
130
|
+
});
|
|
131
|
+
it('adds to a repo base file, bootstrapping it from the template', async () => {
|
|
132
|
+
const result = await addBaseKnowledge('/wsp', 'api', { type: 'gotcha', message: 'flaky test on CI' });
|
|
133
|
+
expect(result.createdFile).toBe(true);
|
|
134
|
+
const content = base.get('api/nexusflow-knowledge.md');
|
|
135
|
+
expect(content).toContain('## Discovered Gotchas & Watch-outs');
|
|
136
|
+
expect(content).toContain('flaky test on CI');
|
|
137
|
+
});
|
|
138
|
+
it('promotes a gotcha to base knowledge (copy leaves the workspace entry)', async () => {
|
|
139
|
+
ws.set('feat/nexusflow-knowledge.md', `# Workspace Knowledge — feat\n\n## Known Gotchas\n\n- **2026-07-04:** shared learning\n`);
|
|
140
|
+
const entries = parseKnowledgeEntries((await readWorkspaceKnowledge('/wsp')));
|
|
141
|
+
const result = await promoteKnowledge('/wsp', { repoName: 'api', entries, mode: 'copy' });
|
|
142
|
+
expect(result.promotedCount).toBe(1);
|
|
143
|
+
expect(base.get('api/nexusflow-knowledge.md')).toContain('- **2026-07-04:** shared learning');
|
|
144
|
+
// Copy mode keeps the original.
|
|
145
|
+
expect(ws.get('feat/nexusflow-knowledge.md')).toContain('- **2026-07-04:** shared learning');
|
|
146
|
+
});
|
|
147
|
+
it('move mode replaces the promoted workspace entry with a note', async () => {
|
|
148
|
+
ws.set('feat/nexusflow-knowledge.md', `# Workspace Knowledge — feat\n\n## Known Gotchas\n\n- **2026-07-04:** shared learning\n`);
|
|
149
|
+
const entries = parseKnowledgeEntries((await readWorkspaceKnowledge('/wsp')));
|
|
150
|
+
await promoteKnowledge('/wsp', { repoName: 'api', entries, mode: 'move' });
|
|
151
|
+
const wsContent = ws.get('feat/nexusflow-knowledge.md');
|
|
152
|
+
expect(wsContent).not.toContain('shared learning');
|
|
153
|
+
expect(wsContent).toContain('Promoted to api base knowledge');
|
|
154
|
+
expect(base.get('api/nexusflow-knowledge.md')).toContain('shared learning');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=knowledge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.test.js","sourceRoot":"","sources":["../../src/core/knowledge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.test.d.ts","sourceRoot":"","sources":["../../src/core/refresh.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { refreshWorkspace } from './refresh.js';
|
|
4
|
+
import * as workspace from './workspace.js';
|
|
5
|
+
import * as analyzers from '../analyzers/index.js';
|
|
6
|
+
import * as generators from '../generators/index.js';
|
|
7
|
+
import * as handoff from '../commands/handoff.js';
|
|
8
|
+
vi.mock('node:fs/promises');
|
|
9
|
+
vi.mock('./workspace.js');
|
|
10
|
+
vi.mock('../analyzers/index.js');
|
|
11
|
+
vi.mock('../generators/index.js');
|
|
12
|
+
vi.mock('../commands/handoff.js');
|
|
13
|
+
const feature = {
|
|
14
|
+
id: 'feat',
|
|
15
|
+
branchName: 'feat',
|
|
16
|
+
description: 'd',
|
|
17
|
+
repos: ['/ws/a', '/ws/b'],
|
|
18
|
+
assistants: ['claude'],
|
|
19
|
+
workspacePath: '/ws',
|
|
20
|
+
createdAt: '2026-07-04T00:00:00Z',
|
|
21
|
+
};
|
|
22
|
+
describe('refreshWorkspace', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
vi.mocked(workspace.loadFeatureConfig).mockResolvedValue(feature);
|
|
26
|
+
vi.mocked(workspace.resolveRepoInfos).mockResolvedValue([
|
|
27
|
+
{ name: 'a', path: '/ws/a', defaultBranch: 'main' },
|
|
28
|
+
{ name: 'b', path: '/ws/b', defaultBranch: 'main' },
|
|
29
|
+
]);
|
|
30
|
+
vi.mocked(analyzers.analyzeAllReposCached).mockResolvedValue({ analysis: new Map(), analyzed: ['a'], reused: ['b'] });
|
|
31
|
+
vi.mocked(generators.generateContextFiles).mockResolvedValue(undefined);
|
|
32
|
+
vi.mocked(handoff.handoffCommand).mockResolvedValue(undefined);
|
|
33
|
+
// No handoff bundle by default.
|
|
34
|
+
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
|
|
35
|
+
});
|
|
36
|
+
it('throws when the workspace manifest is missing', async () => {
|
|
37
|
+
vi.mocked(workspace.loadFeatureConfig).mockResolvedValue(null);
|
|
38
|
+
await expect(refreshWorkspace('/ws')).rejects.toThrow(/Failed to load workspace configuration/);
|
|
39
|
+
});
|
|
40
|
+
it('passes analyzed/reused through to the report', async () => {
|
|
41
|
+
const report = await refreshWorkspace('/ws');
|
|
42
|
+
expect(report.analyzedRepos).toEqual(['a']);
|
|
43
|
+
expect(report.reusedRepos).toEqual(['b']);
|
|
44
|
+
});
|
|
45
|
+
it('limits map regeneration to changed repos by default', async () => {
|
|
46
|
+
await refreshWorkspace('/ws');
|
|
47
|
+
// 6th arg (changedRepos) is the analyzed list when not forcing.
|
|
48
|
+
expect(generators.generateContextFiles).toHaveBeenCalledWith(expect.any(Object), feature.assistants, '/ws', undefined, undefined, ['a']);
|
|
49
|
+
});
|
|
50
|
+
it('passes undefined changed-repos filter when force is set', async () => {
|
|
51
|
+
await refreshWorkspace('/ws', { force: true });
|
|
52
|
+
expect(generators.generateContextFiles).toHaveBeenCalledWith(expect.any(Object), feature.assistants, '/ws', undefined, undefined, undefined);
|
|
53
|
+
});
|
|
54
|
+
it('refreshes the handoff bundle when it exists and not baseOnly', async () => {
|
|
55
|
+
vi.mocked(fs.access).mockResolvedValue(undefined); // handoff file exists
|
|
56
|
+
const report = await refreshWorkspace('/ws');
|
|
57
|
+
expect(handoff.handoffCommand).toHaveBeenCalledWith('/ws');
|
|
58
|
+
expect(report.refreshedHandoff).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('does not touch the handoff bundle when baseOnly', async () => {
|
|
61
|
+
vi.mocked(fs.access).mockResolvedValue(undefined);
|
|
62
|
+
const report = await refreshWorkspace('/ws', { baseOnly: true });
|
|
63
|
+
expect(handoff.handoffCommand).not.toHaveBeenCalled();
|
|
64
|
+
expect(report.refreshedHandoff).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=refresh.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.test.js","sourceRoot":"","sources":["../../src/core/refresh.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/status
|
|
3
|
+
* Headless cross-repo status report for a workspace. Backs the MCP
|
|
4
|
+
* `workspace_status` tool and the `finish` preflight, and can back a GUI
|
|
5
|
+
* status route. Throws only if the workspace manifest cannot be read.
|
|
6
|
+
*/
|
|
7
|
+
import { type RepoStatusFile } from '../utils/multi-git.js';
|
|
8
|
+
/** Full status for a single repo in a workspace. */
|
|
9
|
+
export interface RepoStatusReport {
|
|
10
|
+
name: string;
|
|
11
|
+
path: string;
|
|
12
|
+
/** Current branch, or `null` when the repo is in a detached-HEAD state. */
|
|
13
|
+
branch: string | null;
|
|
14
|
+
/** The feature branch the workspace expects every repo to be on. */
|
|
15
|
+
expectedBranch: string;
|
|
16
|
+
onExpectedBranch: boolean;
|
|
17
|
+
dirty: boolean;
|
|
18
|
+
changedFiles: RepoStatusFile[];
|
|
19
|
+
/** Commits ahead of origin, or `null` when the branch was never pushed. */
|
|
20
|
+
ahead: number | null;
|
|
21
|
+
/** Commits behind origin, or `null` when the branch was never pushed. */
|
|
22
|
+
behind: number | null;
|
|
23
|
+
remoteUrl: string | null;
|
|
24
|
+
defaultBranch: string;
|
|
25
|
+
}
|
|
26
|
+
/** Aggregate workspace status. */
|
|
27
|
+
export interface WorkspaceStatusReport {
|
|
28
|
+
workspacePath: string;
|
|
29
|
+
branchName: string;
|
|
30
|
+
repos: RepoStatusReport[];
|
|
31
|
+
/** True when no repo has working-tree changes. */
|
|
32
|
+
allClean: boolean;
|
|
33
|
+
/** True when every repo is pushed (ahead === 0). A never-pushed branch counts as NOT pushed. */
|
|
34
|
+
allPushed: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Gathers branch, dirtiness, ahead/behind, and remote info for every repo in
|
|
38
|
+
* the workspace.
|
|
39
|
+
*
|
|
40
|
+
* @param workspacePath - Absolute path to the workspace directory.
|
|
41
|
+
* @returns The aggregate {@link WorkspaceStatusReport}.
|
|
42
|
+
* @throws If `nexusflow.json` cannot be read or parsed.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getWorkspaceStatusReport(workspacePath: string): Promise<WorkspaceStatusReport>;
|
|
45
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/core/status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,kCAAkC;AAClC,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,gGAAgG;IAChG,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAkChC"}
|