clitrigger 0.1.12 → 0.1.13
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 +6 -3
- package/README_KR.md +6 -3
- package/dist/client/assets/index-BWNQgE_E.js +649 -0
- package/dist/client/assets/index-Ck_mmrzu.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/server/db/queries.d.ts +39 -3
- package/dist/server/db/queries.d.ts.map +1 -1
- package/dist/server/db/queries.js +118 -3
- package/dist/server/db/queries.js.map +1 -1
- package/dist/server/db/schema.d.ts.map +1 -1
- package/dist/server/db/schema.js +65 -0
- package/dist/server/db/schema.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/favorites.d.ts +3 -0
- package/dist/server/routes/favorites.d.ts.map +1 -0
- package/dist/server/routes/favorites.js +201 -0
- package/dist/server/routes/favorites.js.map +1 -0
- package/dist/server/routes/memory.d.ts.map +1 -1
- package/dist/server/routes/memory.js +396 -4
- package/dist/server/routes/memory.js.map +1 -1
- package/dist/server/routes/projects.js +2 -2
- package/dist/server/routes/projects.js.map +1 -1
- package/dist/server/routes/sessions.d.ts.map +1 -1
- package/dist/server/routes/sessions.js +24 -2
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/services/claude-manager.d.ts +18 -1
- package/dist/server/services/claude-manager.d.ts.map +1 -1
- package/dist/server/services/claude-manager.js +99 -5
- package/dist/server/services/claude-manager.js.map +1 -1
- package/dist/server/services/discussion-orchestrator.d.ts.map +1 -1
- package/dist/server/services/discussion-orchestrator.js +22 -0
- package/dist/server/services/discussion-orchestrator.js.map +1 -1
- package/dist/server/services/memory-ingest.d.ts +16 -0
- package/dist/server/services/memory-ingest.d.ts.map +1 -0
- package/dist/server/services/memory-ingest.js +488 -0
- package/dist/server/services/memory-ingest.js.map +1 -0
- package/dist/server/services/memory-injector.d.ts.map +1 -1
- package/dist/server/services/memory-injector.js +32 -2
- package/dist/server/services/memory-injector.js.map +1 -1
- package/dist/server/services/memory-wikilinks.d.ts +34 -0
- package/dist/server/services/memory-wikilinks.d.ts.map +1 -0
- package/dist/server/services/memory-wikilinks.js +86 -0
- package/dist/server/services/memory-wikilinks.js.map +1 -0
- package/dist/server/services/orchestrator.d.ts.map +1 -1
- package/dist/server/services/orchestrator.js +10 -0
- package/dist/server/services/orchestrator.js.map +1 -1
- package/dist/server/services/session-manager.d.ts +22 -1
- package/dist/server/services/session-manager.d.ts.map +1 -1
- package/dist/server/services/session-manager.js +82 -5
- package/dist/server/services/session-manager.js.map +1 -1
- package/dist/server/websocket/broadcaster.d.ts +11 -0
- package/dist/server/websocket/broadcaster.d.ts.map +1 -1
- package/dist/server/websocket/broadcaster.js +67 -0
- package/dist/server/websocket/broadcaster.js.map +1 -1
- package/dist/server/websocket/events.d.ts +3 -0
- package/dist/server/websocket/events.d.ts.map +1 -1
- package/dist/server/websocket/index.d.ts.map +1 -1
- package/dist/server/websocket/index.js +45 -2
- package/dist/server/websocket/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-CcsvxPmx.css +0 -1
- package/dist/client/assets/index-qSpSlrcM.js +0 -606
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import * as queries from '../db/queries.js';
|
|
5
|
+
const RAW_DIR_NAME = '.clitrigger';
|
|
6
|
+
const RAW_SUBDIR = 'raw';
|
|
7
|
+
const VALID_SOURCE_TYPES = new Set(['todo', 'discussion', 'manual']);
|
|
8
|
+
function ensureGitignore(projectPath, entry) {
|
|
9
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
10
|
+
try {
|
|
11
|
+
const content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
|
|
12
|
+
const lines = content.split(/\r?\n/);
|
|
13
|
+
if (!lines.some(l => l.trim() === entry)) {
|
|
14
|
+
const newline = content.length > 0 && !content.endsWith('\n') ? '\n' : '';
|
|
15
|
+
fs.appendFileSync(gitignorePath, `${newline}${entry}\n`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Non-fatal
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function slugify(input, maxLen = 40) {
|
|
23
|
+
if (!input)
|
|
24
|
+
return 'untitled';
|
|
25
|
+
const cleaned = input
|
|
26
|
+
.replace(/[\\/:*?"<>|]/g, '')
|
|
27
|
+
.replace(/\s+/g, '-')
|
|
28
|
+
.replace(/-+/g, '-')
|
|
29
|
+
.replace(/^[-.]+|[-.]+$/g, '')
|
|
30
|
+
.slice(0, maxLen);
|
|
31
|
+
return cleaned || 'untitled';
|
|
32
|
+
}
|
|
33
|
+
function timestampStr(d = new Date()) {
|
|
34
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
35
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Write the raw source text to <projectPath>/.clitrigger/raw/<sourceType>/<file>.md
|
|
39
|
+
* and return the project-relative path. Returns null on failure (non-fatal).
|
|
40
|
+
*/
|
|
41
|
+
function writeRawSnapshot(project, sourceType, sourceId, fullText, titleHint) {
|
|
42
|
+
if (!VALID_SOURCE_TYPES.has(sourceType))
|
|
43
|
+
return null;
|
|
44
|
+
if (!project.path)
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const baseDir = path.join(project.path, RAW_DIR_NAME, RAW_SUBDIR, sourceType);
|
|
48
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
49
|
+
ensureGitignore(project.path, `${RAW_DIR_NAME}/`);
|
|
50
|
+
const ts = timestampStr();
|
|
51
|
+
const idPart = sourceId ? `-${sourceId.slice(0, 8)}` : '';
|
|
52
|
+
const slug = slugify(titleHint || sourceType);
|
|
53
|
+
const filename = `${ts}${idPart}-${slug}.md`;
|
|
54
|
+
const filePath = path.join(baseDir, filename);
|
|
55
|
+
// Defensive: ensure final resolved path is still inside baseDir
|
|
56
|
+
const resolvedFinal = path.resolve(filePath);
|
|
57
|
+
const resolvedBase = path.resolve(baseDir);
|
|
58
|
+
if (!resolvedFinal.startsWith(resolvedBase + path.sep) && resolvedFinal !== resolvedBase) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
fs.writeFileSync(filePath, fullText, 'utf-8');
|
|
62
|
+
const rel = path.relative(project.path, filePath).split(path.sep).join('/');
|
|
63
|
+
return rel;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.warn('[memory-ingest] writeRawSnapshot failed:', err);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const DEFAULT_WIKI_SCHEMA = `# Wiki Schema
|
|
71
|
+
|
|
72
|
+
## Entity Types
|
|
73
|
+
- **Feature** — product capabilities and implemented behaviors
|
|
74
|
+
- **Decision** — architectural/design choices with rationale
|
|
75
|
+
- **Bug** — known issues, root causes, and workarounds
|
|
76
|
+
- **Pattern** — reusable code/design patterns
|
|
77
|
+
- **Concept** — domain knowledge and terminology
|
|
78
|
+
|
|
79
|
+
## Conventions
|
|
80
|
+
- Titles: short noun phrases (≤60 chars)
|
|
81
|
+
- Body: 2-5 sentences, factual, no filler
|
|
82
|
+
- **Use [[Title]] wikilinks liberally inside body text** — every time the body mentions another node by name, wrap it as [[Title]]. This is the primary way connections are made.
|
|
83
|
+
- Tags: first tag should be the entity type
|
|
84
|
+
- Prefer updating existing nodes over creating new duplicates
|
|
85
|
+
- Connections are the value of a wiki — a node with no inbound or outbound links is nearly useless. Always relate new entries to existing ones.`;
|
|
86
|
+
const WIKI_SCHEMA_TAG = '__wiki_schema__';
|
|
87
|
+
function stripCodeFences(text) {
|
|
88
|
+
const trimmed = text.trim();
|
|
89
|
+
const m = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/);
|
|
90
|
+
return m ? m[1].trim() : trimmed;
|
|
91
|
+
}
|
|
92
|
+
function safeParseIngestOp(raw) {
|
|
93
|
+
const empty = { create: [], update: [], edges: [] };
|
|
94
|
+
const cleaned = stripCodeFences(raw);
|
|
95
|
+
let parsed;
|
|
96
|
+
try {
|
|
97
|
+
parsed = JSON.parse(cleaned);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
const m = cleaned.match(/\{[\s\S]*\}/);
|
|
101
|
+
if (!m)
|
|
102
|
+
return empty;
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(m[0]);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return empty;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
111
|
+
return empty;
|
|
112
|
+
const p = parsed;
|
|
113
|
+
const create = Array.isArray(p.create) ? p.create : [];
|
|
114
|
+
const update = Array.isArray(p.update) ? p.update : [];
|
|
115
|
+
const edges = Array.isArray(p.edges) ? p.edges : [];
|
|
116
|
+
return { create, update, edges };
|
|
117
|
+
}
|
|
118
|
+
function safeParseLintIssues(raw) {
|
|
119
|
+
const cleaned = stripCodeFences(raw);
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = JSON.parse(cleaned);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
const m = cleaned.match(/\[[\s\S]*\]/);
|
|
126
|
+
if (!m)
|
|
127
|
+
return [];
|
|
128
|
+
try {
|
|
129
|
+
parsed = JSON.parse(m[0]);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(parsed))
|
|
136
|
+
return [];
|
|
137
|
+
const valid = ['contradiction', 'orphan', 'duplicate', 'stale'];
|
|
138
|
+
return parsed
|
|
139
|
+
.filter((e) => e && typeof e === 'object')
|
|
140
|
+
.filter(e => valid.includes(String(e.type)))
|
|
141
|
+
.map(e => ({
|
|
142
|
+
type: e.type,
|
|
143
|
+
node_titles: Array.isArray(e.node_titles) ? e.node_titles.map(String) : [],
|
|
144
|
+
message: typeof e.message === 'string' ? e.message.trim() : '',
|
|
145
|
+
}))
|
|
146
|
+
.filter(e => e.message)
|
|
147
|
+
.slice(0, 10);
|
|
148
|
+
}
|
|
149
|
+
function buildInvocation(cliTool) {
|
|
150
|
+
switch (cliTool) {
|
|
151
|
+
case 'gemini': return { command: 'gemini', args: ['--yolo', '--prompt='] };
|
|
152
|
+
case 'codex': return { command: 'codex', args: ['exec'] };
|
|
153
|
+
case 'claude':
|
|
154
|
+
default: return { command: 'claude', args: ['--print'] };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function runHeadless(cliTool, prompt, timeoutMs = 180_000) {
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const { command, args } = buildInvocation(cliTool);
|
|
160
|
+
const isWin = process.platform === 'win32';
|
|
161
|
+
const spawnCmd = isWin ? 'cmd.exe' : command;
|
|
162
|
+
const spawnArgs = isWin ? ['/c', command, ...args] : args;
|
|
163
|
+
let stdout = '';
|
|
164
|
+
let stderr = '';
|
|
165
|
+
let settled = false;
|
|
166
|
+
const proc = spawn(spawnCmd, spawnArgs, {
|
|
167
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
168
|
+
env: { ...process.env },
|
|
169
|
+
cwd: process.env.HOME || process.env.USERPROFILE || '.',
|
|
170
|
+
});
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
if (settled)
|
|
173
|
+
return;
|
|
174
|
+
settled = true;
|
|
175
|
+
try {
|
|
176
|
+
proc.kill();
|
|
177
|
+
}
|
|
178
|
+
catch { /* ignore */ }
|
|
179
|
+
reject(new Error('Memory ingest timed out'));
|
|
180
|
+
}, timeoutMs);
|
|
181
|
+
proc.stdout.on('data', (c) => { stdout += c.toString('utf8'); });
|
|
182
|
+
proc.stderr.on('data', (c) => { stderr += c.toString('utf8'); });
|
|
183
|
+
proc.on('error', (err) => {
|
|
184
|
+
if (settled)
|
|
185
|
+
return;
|
|
186
|
+
settled = true;
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
reject(err);
|
|
189
|
+
});
|
|
190
|
+
proc.on('close', (code) => {
|
|
191
|
+
if (settled)
|
|
192
|
+
return;
|
|
193
|
+
settled = true;
|
|
194
|
+
clearTimeout(timer);
|
|
195
|
+
if (code === 0)
|
|
196
|
+
resolve(stdout);
|
|
197
|
+
else
|
|
198
|
+
reject(new Error(`CLI exited with code ${code}: ${stderr.trim().slice(0, 300)}`));
|
|
199
|
+
});
|
|
200
|
+
try {
|
|
201
|
+
proc.stdin.write(prompt + '\n');
|
|
202
|
+
proc.stdin.end();
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
if (settled)
|
|
206
|
+
return;
|
|
207
|
+
settled = true;
|
|
208
|
+
clearTimeout(timer);
|
|
209
|
+
try {
|
|
210
|
+
proc.kill();
|
|
211
|
+
}
|
|
212
|
+
catch { /* ignore */ }
|
|
213
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function resolveCliTool(value) {
|
|
218
|
+
if (value === 'claude' || value === 'gemini' || value === 'codex')
|
|
219
|
+
return value;
|
|
220
|
+
return 'claude';
|
|
221
|
+
}
|
|
222
|
+
function getOrCreateSchemaNode(projectId) {
|
|
223
|
+
const all = queries.getMemoryNodesByProjectId(projectId);
|
|
224
|
+
const existing = all.find(n => {
|
|
225
|
+
try {
|
|
226
|
+
const tags = JSON.parse(n.tags ?? '[]');
|
|
227
|
+
return Array.isArray(tags) && tags.includes(WIKI_SCHEMA_TAG);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
if (existing)
|
|
234
|
+
return existing.body || DEFAULT_WIKI_SCHEMA;
|
|
235
|
+
queries.createMemoryNode(projectId, 'Wiki Schema', DEFAULT_WIKI_SCHEMA, JSON.stringify([WIKI_SCHEMA_TAG]), 1);
|
|
236
|
+
return DEFAULT_WIKI_SCHEMA;
|
|
237
|
+
}
|
|
238
|
+
function buildNodeSummary(nodes) {
|
|
239
|
+
const visible = nodes.filter(n => {
|
|
240
|
+
try {
|
|
241
|
+
const tags = JSON.parse(n.tags ?? '[]');
|
|
242
|
+
return !Array.isArray(tags) || !tags.includes(WIKI_SCHEMA_TAG);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
if (visible.length === 0)
|
|
249
|
+
return '(no existing pages)';
|
|
250
|
+
return visible.map(n => {
|
|
251
|
+
try {
|
|
252
|
+
const tags = JSON.parse(n.tags ?? '[]');
|
|
253
|
+
const tagStr = tags.filter(t => t !== WIKI_SCHEMA_TAG).join(', ');
|
|
254
|
+
const pinned = n.pinned ? ' [pinned]' : '';
|
|
255
|
+
const bodyPreview = n.pinned ? `\n ${(n.body || '').slice(0, 300)}` : '';
|
|
256
|
+
return `- id="${n.id}" title="${n.title}"${tagStr ? ` tags=[${tagStr}]` : ''}${pinned}${bodyPreview}`;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return `- id="${n.id}" title="${n.title}"`;
|
|
260
|
+
}
|
|
261
|
+
}).join('\n');
|
|
262
|
+
}
|
|
263
|
+
const INGEST_PROMPT_HEADER = `You are maintaining a project knowledge wiki using the LLM Wiki pattern.
|
|
264
|
+
|
|
265
|
+
## Wiki Schema
|
|
266
|
+
{SCHEMA}
|
|
267
|
+
|
|
268
|
+
## Existing Wiki Pages
|
|
269
|
+
{NODES}
|
|
270
|
+
|
|
271
|
+
## New Source Material
|
|
272
|
+
{SOURCE}
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
Analyze the source material and output ONLY a JSON object (no prose, no code fences):
|
|
277
|
+
{
|
|
278
|
+
"create": [{"title": "string", "body": "string", "tags": ["string"]}],
|
|
279
|
+
"update": [{"id": "string", "body": "string", "tags": ["string"]}],
|
|
280
|
+
"edges": [{"from_title": "string", "to_title": "string", "relation_type": "related", "label": "string"}]
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
Rules:
|
|
284
|
+
- 0-10 total create+update operations. Quality over quantity. Skip if nothing new.
|
|
285
|
+
- Match existing nodes by title (case-insensitive) before creating a new one.
|
|
286
|
+
- body: 2-5 sentences, factual, no filler. Markdown allowed.
|
|
287
|
+
- **Inside body, wrap every reference to another node as [[Exact Title]].** When you mention a node that exists in "Existing Wiki Pages" or that you are creating in this batch, write [[Title]] instead of plain text. Aim for 1-3 wikilinks per body when relevant nodes exist. Bodies that mention concepts but don't link them are low quality.
|
|
288
|
+
- tags[0] must be an entity type from the schema (Feature/Decision/Bug/Pattern/Concept).
|
|
289
|
+
- edges.relation_type: one of related|precedes|example_of|counter_example|refines
|
|
290
|
+
- related: generic association
|
|
291
|
+
- precedes: A comes before B in time/sequence/dependency
|
|
292
|
+
- example_of: A is a concrete instance of pattern/concept B
|
|
293
|
+
- counter_example: A contradicts or is rejected in favor of B
|
|
294
|
+
- refines: A is a more specific or improved version of B
|
|
295
|
+
- **Edges are the value of a wiki — generate them aggressively.** For each new or updated node, link it to at least one existing or newly-created node when topically related. If multiple relations apply (A is both an example_of and precedes B), pick the most specific one. from_title/to_title must match existing or newly created nodes exactly. Do not invent titles.
|
|
296
|
+
- A node with zero inbound and outbound connections (no edges, no wikilinks pointing to or from it) is a code smell — fix it before output.
|
|
297
|
+
- If nothing worth extracting, return {"create": [], "update": [], "edges": []}`;
|
|
298
|
+
const LINT_PROMPT_HEADER = `You are auditing a project knowledge wiki for quality issues.
|
|
299
|
+
|
|
300
|
+
## Wiki Pages
|
|
301
|
+
{NODES}
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
Output ONLY a JSON array (no prose, no code fences):
|
|
306
|
+
[{"type": "contradiction|orphan|duplicate|stale", "node_titles": ["title1", "title2"], "message": "short description"}]
|
|
307
|
+
|
|
308
|
+
Issue types:
|
|
309
|
+
- contradiction: two nodes make conflicting claims
|
|
310
|
+
- orphan: node has no connections and seems isolated/useless
|
|
311
|
+
- duplicate: two nodes cover the same topic and should be merged
|
|
312
|
+
- stale: node references something that seems outdated or removed
|
|
313
|
+
|
|
314
|
+
Rules:
|
|
315
|
+
- Maximum 10 issues. Only flag real problems.
|
|
316
|
+
- Return [] if the wiki looks healthy.`;
|
|
317
|
+
export async function ingestSource(projectId, sourceText, sourceType, sourceId, titleHint) {
|
|
318
|
+
const project = queries.getProjectById(projectId);
|
|
319
|
+
if (!project)
|
|
320
|
+
throw new Error('Project not found');
|
|
321
|
+
const cliTool = resolveCliTool(project.cli_tool);
|
|
322
|
+
// Step 1: persist raw snapshot (immutable). Failure is non-fatal.
|
|
323
|
+
let rawPath = null;
|
|
324
|
+
if (sourceType && VALID_SOURCE_TYPES.has(sourceType)) {
|
|
325
|
+
const hint = (titleHint && titleHint.trim()) || sourceText.split('\n').find(l => l.trim())?.trim().slice(0, 60) || sourceType;
|
|
326
|
+
rawPath = writeRawSnapshot(project, sourceType, sourceId, sourceText, hint);
|
|
327
|
+
}
|
|
328
|
+
const schema = getOrCreateSchemaNode(projectId);
|
|
329
|
+
const nodes = queries.getMemoryNodesByProjectId(projectId);
|
|
330
|
+
const nodeSummary = buildNodeSummary(nodes);
|
|
331
|
+
const prompt = INGEST_PROMPT_HEADER
|
|
332
|
+
.replace('{SCHEMA}', schema)
|
|
333
|
+
.replace('{NODES}', nodeSummary)
|
|
334
|
+
.replace('{SOURCE}', sourceText.slice(0, 8000));
|
|
335
|
+
const raw = await runHeadless(cliTool, prompt);
|
|
336
|
+
const op = safeParseIngestOp(raw);
|
|
337
|
+
const titleToId = new Map(nodes.map(n => [n.title.toLowerCase(), n.id]));
|
|
338
|
+
const createdIds = [];
|
|
339
|
+
let edgesAdded = 0;
|
|
340
|
+
const VALID_RELATIONS = new Set(['related', 'precedes', 'example_of', 'counter_example', 'refines']);
|
|
341
|
+
for (const c of op.create.slice(0, 10)) {
|
|
342
|
+
if (!c.title?.trim())
|
|
343
|
+
continue;
|
|
344
|
+
const title = String(c.title).trim().slice(0, 120);
|
|
345
|
+
if (titleToId.has(title.toLowerCase()))
|
|
346
|
+
continue;
|
|
347
|
+
try {
|
|
348
|
+
const tags = Array.isArray(c.tags) ? JSON.stringify(c.tags.map(String).filter(Boolean)) : null;
|
|
349
|
+
const node = queries.createMemoryNode(projectId, title, typeof c.body === 'string' ? c.body : '', tags, 0, sourceType, sourceId, rawPath);
|
|
350
|
+
titleToId.set(title.toLowerCase(), node.id);
|
|
351
|
+
createdIds.push(node.id);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
/* skip on UNIQUE conflict */
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
let updatedCount = 0;
|
|
358
|
+
for (const u of op.update.slice(0, 10)) {
|
|
359
|
+
if (!u.id)
|
|
360
|
+
continue;
|
|
361
|
+
const existing = nodes.find(n => n.id === u.id);
|
|
362
|
+
if (!existing)
|
|
363
|
+
continue;
|
|
364
|
+
const upd = {};
|
|
365
|
+
if (typeof u.body === 'string')
|
|
366
|
+
upd.body = u.body;
|
|
367
|
+
if (Array.isArray(u.tags))
|
|
368
|
+
upd.tags = JSON.stringify(u.tags.map(String).filter(Boolean));
|
|
369
|
+
if (Object.keys(upd).length > 0) {
|
|
370
|
+
queries.updateMemoryNode(u.id, upd);
|
|
371
|
+
updatedCount++;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (const e of op.edges.slice(0, 20)) {
|
|
375
|
+
const fromId = titleToId.get(String(e.from_title || '').toLowerCase());
|
|
376
|
+
const toId = titleToId.get(String(e.to_title || '').toLowerCase());
|
|
377
|
+
if (!fromId || !toId || fromId === toId)
|
|
378
|
+
continue;
|
|
379
|
+
const rt = VALID_RELATIONS.has(e.relation_type ?? '') ? e.relation_type : 'related';
|
|
380
|
+
try {
|
|
381
|
+
queries.createMemoryEdge(projectId, fromId, toId, rt, e.label ?? null);
|
|
382
|
+
edgesAdded++;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
/* skip duplicates */
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
created: createdIds.length,
|
|
390
|
+
updated: updatedCount,
|
|
391
|
+
edgesAdded,
|
|
392
|
+
nodeIds: createdIds,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
export async function lintWiki(projectId) {
|
|
396
|
+
const project = queries.getProjectById(projectId);
|
|
397
|
+
if (!project)
|
|
398
|
+
throw new Error('Project not found');
|
|
399
|
+
const cliTool = resolveCliTool(project.cli_tool);
|
|
400
|
+
const nodes = queries.getMemoryNodesByProjectId(projectId);
|
|
401
|
+
const visible = nodes.filter(n => {
|
|
402
|
+
try {
|
|
403
|
+
const tags = JSON.parse(n.tags ?? '[]');
|
|
404
|
+
return !Array.isArray(tags) || !tags.includes(WIKI_SCHEMA_TAG);
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
if (visible.length === 0)
|
|
411
|
+
return [];
|
|
412
|
+
const edges = queries.getMemoryEdgesByProjectId(projectId);
|
|
413
|
+
const edgeSet = new Set(edges.flatMap(e => [e.from_node_id, e.to_node_id]));
|
|
414
|
+
const nodeText = visible.map(n => {
|
|
415
|
+
const body = (n.body || '').slice(0, 400);
|
|
416
|
+
const hasEdge = edgeSet.has(n.id) ? '' : ' [no-edges]';
|
|
417
|
+
return `### ${n.title}${hasEdge}\n${body}`;
|
|
418
|
+
}).join('\n\n');
|
|
419
|
+
const prompt = LINT_PROMPT_HEADER.replace('{NODES}', nodeText.slice(0, 12000));
|
|
420
|
+
const raw = await runHeadless(cliTool, prompt);
|
|
421
|
+
return safeParseLintIssues(raw);
|
|
422
|
+
}
|
|
423
|
+
export function buildSourceTextFromTodo(todoId) {
|
|
424
|
+
const todo = queries.getTodoById(todoId);
|
|
425
|
+
if (!todo)
|
|
426
|
+
return null;
|
|
427
|
+
const logs = queries.getTaskLogsByTodoId(todoId);
|
|
428
|
+
if (logs.length === 0)
|
|
429
|
+
return null;
|
|
430
|
+
const assistantLogs = logs
|
|
431
|
+
.filter(l => l.log_type === 'assistant' && l.message.trim())
|
|
432
|
+
.sort((a, b) => (a.round_number ?? 1) - (b.round_number ?? 1));
|
|
433
|
+
if (assistantLogs.length === 0)
|
|
434
|
+
return null;
|
|
435
|
+
const lines = [];
|
|
436
|
+
lines.push(`# Task: ${todo.title}`);
|
|
437
|
+
if (todo.description) {
|
|
438
|
+
lines.push('');
|
|
439
|
+
lines.push('## Description');
|
|
440
|
+
lines.push(todo.description.trim());
|
|
441
|
+
}
|
|
442
|
+
lines.push('');
|
|
443
|
+
// Group by round
|
|
444
|
+
const byRound = new Map();
|
|
445
|
+
for (const l of assistantLogs) {
|
|
446
|
+
const r = l.round_number ?? 1;
|
|
447
|
+
if (!byRound.has(r))
|
|
448
|
+
byRound.set(r, []);
|
|
449
|
+
byRound.get(r).push(l.message.trim());
|
|
450
|
+
}
|
|
451
|
+
for (const [round, msgs] of [...byRound.entries()].sort(([a], [b]) => a - b)) {
|
|
452
|
+
lines.push(`## Round ${round}`);
|
|
453
|
+
lines.push(msgs.join('\n\n'));
|
|
454
|
+
lines.push('');
|
|
455
|
+
}
|
|
456
|
+
return lines.join('\n');
|
|
457
|
+
}
|
|
458
|
+
export function buildSourceTextFromDiscussion(discussionId) {
|
|
459
|
+
const discussion = queries.getDiscussionById(discussionId);
|
|
460
|
+
if (!discussion)
|
|
461
|
+
return null;
|
|
462
|
+
const messages = queries.getDiscussionMessages(discussionId);
|
|
463
|
+
if (messages.length === 0)
|
|
464
|
+
return null;
|
|
465
|
+
const completed = messages.filter(m => m.status === 'completed' && m.content && m.content.trim());
|
|
466
|
+
if (completed.length === 0)
|
|
467
|
+
return null;
|
|
468
|
+
const lines = [];
|
|
469
|
+
lines.push(`# Discussion: ${discussion.title}`);
|
|
470
|
+
if (discussion.description) {
|
|
471
|
+
lines.push('');
|
|
472
|
+
lines.push('## Description');
|
|
473
|
+
lines.push(discussion.description.trim());
|
|
474
|
+
}
|
|
475
|
+
lines.push('');
|
|
476
|
+
let lastRound = -1;
|
|
477
|
+
for (const m of completed) {
|
|
478
|
+
if (m.round_number !== lastRound) {
|
|
479
|
+
lines.push(`## Round ${m.round_number}`);
|
|
480
|
+
lastRound = m.round_number;
|
|
481
|
+
}
|
|
482
|
+
lines.push(`### ${m.agent_name} (${m.role})`);
|
|
483
|
+
lines.push((m.content ?? '').trim());
|
|
484
|
+
lines.push('');
|
|
485
|
+
}
|
|
486
|
+
return lines.join('\n');
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=memory-ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-ingest.js","sourceRoot":"","sources":["../../../src/server/services/memory-ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-injector.d.ts","sourceRoot":"","sources":["../../../src/server/services/memory-injector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-injector.d.ts","sourceRoot":"","sources":["../../../src/server/services/memory-injector.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,sBAAsB,GAAG,qBAAqB,GAAG,IAAI,CAuB1F;AA0ED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAQ3E"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as queries from '../db/queries.js';
|
|
2
|
+
import { parseWikilinks } from './memory-wikilinks.js';
|
|
2
3
|
export function buildMemoryBlock(req) {
|
|
3
4
|
if (req.mode === 'none')
|
|
4
5
|
return null;
|
|
@@ -26,15 +27,22 @@ export function buildMemoryBlock(req) {
|
|
|
26
27
|
function formatMemoryBlock(nodes, edges) {
|
|
27
28
|
const lines = [];
|
|
28
29
|
lines.push('<long_term_memory>');
|
|
29
|
-
lines.push('You have access to the following long-term project memory. Treat each <memory_node> as authoritative reference material curated by the user. Apply it where relevant; you may quote IDs (e.g. "per memory_node:abc123") when explaining decisions, but do not echo entire bodies verbatim unless asked.');
|
|
30
|
+
lines.push('You have access to the following long-term project memory. Treat each <memory_node> as authoritative reference material curated by the user. Apply it where relevant; you may quote IDs (e.g. "per memory_node:abc123") when explaining decisions, but do not echo entire bodies verbatim unless asked. Wikilinks in the form [[#nodeId:Title]] reference other nodes in this block; an unresolved [[Title]] indicates a node that has not been created yet.');
|
|
30
31
|
lines.push('');
|
|
32
|
+
// Build a project-wide title→id index so wikilinks can be normalized to [[#id:title]]
|
|
33
|
+
const projectId = nodes[0]?.project_id;
|
|
34
|
+
const projectNodes = projectId ? queries.getMemoryNodesByProjectId(projectId) : [];
|
|
35
|
+
const titleIndex = new Map();
|
|
36
|
+
for (const pn of projectNodes) {
|
|
37
|
+
titleIndex.set(pn.title.toLowerCase(), pn.id);
|
|
38
|
+
}
|
|
31
39
|
for (const n of nodes) {
|
|
32
40
|
const titleAttr = escapeAttr(n.title);
|
|
33
41
|
const tagsAttr = parseTagsList(n.tags);
|
|
34
42
|
const tagsAttrStr = tagsAttr.length > 0 ? ` tags="${escapeAttr(tagsAttr.join(','))}"` : '';
|
|
35
43
|
lines.push(`<memory_node id="${n.id}" title="${titleAttr}"${tagsAttrStr}>`);
|
|
36
44
|
if (n.body)
|
|
37
|
-
lines.push(n.body);
|
|
45
|
+
lines.push(normalizeWikilinks(n.body, titleIndex));
|
|
38
46
|
lines.push('</memory_node>');
|
|
39
47
|
}
|
|
40
48
|
if (edges.length > 0) {
|
|
@@ -52,6 +60,28 @@ function formatMemoryBlock(nodes, edges) {
|
|
|
52
60
|
function escapeAttr(s) {
|
|
53
61
|
return s.replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
54
62
|
}
|
|
63
|
+
function normalizeWikilinks(body, titleIndex) {
|
|
64
|
+
const refs = parseWikilinks(body);
|
|
65
|
+
if (refs.length === 0)
|
|
66
|
+
return body;
|
|
67
|
+
// Walk the body in order, splicing each match with its resolved/unresolved replacement
|
|
68
|
+
const out = [];
|
|
69
|
+
let cursor = 0;
|
|
70
|
+
for (const ref of refs) {
|
|
71
|
+
out.push(body.slice(cursor, ref.start));
|
|
72
|
+
const id = titleIndex.get(ref.title.toLowerCase());
|
|
73
|
+
if (id) {
|
|
74
|
+
const aliasPart = ref.alias ? `|${ref.alias}` : '';
|
|
75
|
+
out.push(`[[#${id}:${ref.title}${aliasPart}]]`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
out.push(ref.raw); // leave unresolved as-is (still readable to the LLM)
|
|
79
|
+
}
|
|
80
|
+
cursor = ref.end;
|
|
81
|
+
}
|
|
82
|
+
out.push(body.slice(cursor));
|
|
83
|
+
return out.join('');
|
|
84
|
+
}
|
|
55
85
|
function parseTagsList(tagsJson) {
|
|
56
86
|
if (!tagsJson)
|
|
57
87
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-injector.js","sourceRoot":"","sources":["../../../src/server/services/memory-injector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-injector.js","sourceRoot":"","sources":["../../../src/server/services/memory-injector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MemoryNode } from '../db/queries.js';
|
|
2
|
+
export interface WikilinkRef {
|
|
3
|
+
raw: string;
|
|
4
|
+
title: string;
|
|
5
|
+
alias?: string;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseWikilinks(body: string): WikilinkRef[];
|
|
10
|
+
export interface ResolvedWikilink {
|
|
11
|
+
title: string;
|
|
12
|
+
nodeId: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveWikilinks(projectId: string, titles: string[]): ResolvedWikilink[];
|
|
15
|
+
/**
|
|
16
|
+
* Replace `[[oldTitle]]` (case-insensitive title match) with `[[newTitle]]`,
|
|
17
|
+
* preserving any alias (`|alias`) or heading (`#section`) suffix the user wrote.
|
|
18
|
+
*/
|
|
19
|
+
export declare function replaceTitleInBody(body: string, oldTitle: string, newTitle: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Find every node whose body references `targetTitle` via `[[targetTitle]]`,
|
|
22
|
+
* returning the source node + a 60-char snippet around the first match.
|
|
23
|
+
*/
|
|
24
|
+
export interface BacklinkHit {
|
|
25
|
+
source: MemoryNode;
|
|
26
|
+
snippet: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function findBacklinks(projectId: string, targetTitle: string, excludeNodeId?: string): BacklinkHit[];
|
|
29
|
+
/**
|
|
30
|
+
* Append `[[targetTitle]]` to the end of an existing body, separated by a blank line
|
|
31
|
+
* so it doesn't accidentally fuse with prior markdown. If the link already exists, no-op.
|
|
32
|
+
*/
|
|
33
|
+
export declare function appendWikilinkToBody(body: string, targetTitle: string): string;
|
|
34
|
+
//# sourceMappingURL=memory-wikilinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-wikilinks.d.ts","sourceRoot":"","sources":["../../../src/server/services/memory-wikilinks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAID,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAiB1D;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAWxF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK3F;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAa3G;AAWD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAO9E"}
|