docdex 0.2.18 → 0.2.19
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/CHANGELOG.md +5 -0
- package/lib/postinstall_setup.js +176 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.19
|
|
4
|
+
- Playwright issue fix
|
|
5
|
+
- Agents md adding command manually
|
|
6
|
+
- Agents md append repeat fix
|
|
7
|
+
|
|
3
8
|
## 0.2.16
|
|
4
9
|
- Repo memory now tags items with `repoId` and filters recalls to prevent cross-repo leakage in multi-repo daemons.
|
|
5
10
|
- MCP HTTP requires explicit repo selection when multiple repos are active.
|
package/lib/postinstall_setup.js
CHANGED
|
@@ -20,6 +20,8 @@ const DEFAULT_OLLAMA_CHAT_MODEL = "phi3.5:3.8b";
|
|
|
20
20
|
const DEFAULT_OLLAMA_CHAT_MODEL_SIZE_GIB = 2.2;
|
|
21
21
|
const SETUP_PENDING_MARKER = "setup_pending.json";
|
|
22
22
|
const AGENTS_DOC_FILENAME = "agents.md";
|
|
23
|
+
const DOCDEX_INFO_START_PREFIX = "---- START OF DOCDEX INFO V";
|
|
24
|
+
const DOCDEX_INFO_END = "---- END OF DOCDEX INFO -----";
|
|
23
25
|
|
|
24
26
|
function defaultConfigPath() {
|
|
25
27
|
return path.join(os.homedir(), ".docdex", "config.toml");
|
|
@@ -158,6 +160,18 @@ function agentsDocSourcePath() {
|
|
|
158
160
|
return path.join(__dirname, "..", "assets", AGENTS_DOC_FILENAME);
|
|
159
161
|
}
|
|
160
162
|
|
|
163
|
+
function resolvePackageVersion() {
|
|
164
|
+
const packagePath = path.join(__dirname, "..", "package.json");
|
|
165
|
+
if (!fs.existsSync(packagePath)) return "unknown";
|
|
166
|
+
try {
|
|
167
|
+
const raw = fs.readFileSync(packagePath, "utf8");
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : "unknown";
|
|
170
|
+
} catch {
|
|
171
|
+
return "unknown";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
161
175
|
function loadAgentInstructions() {
|
|
162
176
|
const sourcePath = agentsDocSourcePath();
|
|
163
177
|
if (!fs.existsSync(sourcePath)) return "";
|
|
@@ -172,13 +186,123 @@ function normalizeInstructionText(value) {
|
|
|
172
186
|
return String(value || "").trim();
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
function
|
|
189
|
+
function docdexBlockStart(version) {
|
|
190
|
+
return `${DOCDEX_INFO_START_PREFIX}${version} ----`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function buildDocdexInstructionBlock(instructions) {
|
|
194
|
+
const next = normalizeInstructionText(instructions);
|
|
195
|
+
if (!next) return "";
|
|
196
|
+
const version = resolvePackageVersion();
|
|
197
|
+
return `${docdexBlockStart(version)}\n${next}\n${DOCDEX_INFO_END}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function extractDocdexBlockBody(text) {
|
|
201
|
+
const match = String(text || "").match(
|
|
202
|
+
new RegExp(
|
|
203
|
+
`${escapeRegExp(DOCDEX_INFO_START_PREFIX)}[^\\r\\n]* ----\\r?\\n([\\s\\S]*?)\\r?\\n${escapeRegExp(
|
|
204
|
+
DOCDEX_INFO_END
|
|
205
|
+
)}`
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
return match ? normalizeInstructionText(match[1]) : "";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function extractDocdexBlockVersion(text) {
|
|
212
|
+
const match = String(text || "").match(
|
|
213
|
+
new RegExp(`${escapeRegExp(DOCDEX_INFO_START_PREFIX)}([^\\s]+) ----`)
|
|
214
|
+
);
|
|
215
|
+
return match ? match[1] : null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function hasDocdexBlockVersion(text, version) {
|
|
219
|
+
if (!version) return false;
|
|
220
|
+
return String(text || "").includes(docdexBlockStart(version));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function stripDocdexBlocks(text) {
|
|
224
|
+
const re = new RegExp(
|
|
225
|
+
`${escapeRegExp(DOCDEX_INFO_START_PREFIX)}[^\\r\\n]* ----\\r?\\n[\\s\\S]*?\\r?\\n${escapeRegExp(
|
|
226
|
+
DOCDEX_INFO_END
|
|
227
|
+
)}\\r?\\n?`,
|
|
228
|
+
"g"
|
|
229
|
+
);
|
|
230
|
+
return String(text || "").replace(re, "").trim();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function stripDocdexBlocksExcept(text, version) {
|
|
234
|
+
if (!version) return stripDocdexBlocks(text);
|
|
235
|
+
const source = String(text || "");
|
|
236
|
+
const re = new RegExp(
|
|
237
|
+
`${escapeRegExp(DOCDEX_INFO_START_PREFIX)}[^\\r\\n]* ----\\r?\\n[\\s\\S]*?\\r?\\n${escapeRegExp(
|
|
238
|
+
DOCDEX_INFO_END
|
|
239
|
+
)}\\r?\\n?`,
|
|
240
|
+
"g"
|
|
241
|
+
);
|
|
242
|
+
let result = "";
|
|
243
|
+
let lastIndex = 0;
|
|
244
|
+
let match;
|
|
245
|
+
while ((match = re.exec(source))) {
|
|
246
|
+
const before = source.slice(lastIndex, match.index);
|
|
247
|
+
result += before;
|
|
248
|
+
const block = match[0];
|
|
249
|
+
const blockVersion = extractDocdexBlockVersion(block);
|
|
250
|
+
if (blockVersion === version) {
|
|
251
|
+
result += block;
|
|
252
|
+
}
|
|
253
|
+
lastIndex = match.index + block.length;
|
|
254
|
+
}
|
|
255
|
+
result += source.slice(lastIndex);
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function stripLegacyDocdexBodySegment(segment, body) {
|
|
260
|
+
if (!body) return String(segment || "");
|
|
261
|
+
const normalizedSegment = String(segment || "").replace(/\r\n/g, "\n");
|
|
262
|
+
const normalizedBody = String(body || "").replace(/\r\n/g, "\n");
|
|
263
|
+
if (!normalizedBody.trim()) return normalizedSegment;
|
|
264
|
+
const re = new RegExp(`\\n?${escapeRegExp(normalizedBody)}\\n?`, "g");
|
|
265
|
+
return normalizedSegment.replace(re, "\n").replace(/\n{3,}/g, "\n\n");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function stripLegacyDocdexBody(text, body) {
|
|
269
|
+
if (!body) return String(text || "");
|
|
270
|
+
const source = String(text || "").replace(/\r\n/g, "\n");
|
|
271
|
+
const re = new RegExp(
|
|
272
|
+
`${escapeRegExp(DOCDEX_INFO_START_PREFIX)}[^\\n]* ----\\n[\\s\\S]*?\\n${escapeRegExp(DOCDEX_INFO_END)}\\n?`,
|
|
273
|
+
"g"
|
|
274
|
+
);
|
|
275
|
+
let result = "";
|
|
276
|
+
let lastIndex = 0;
|
|
277
|
+
let match;
|
|
278
|
+
while ((match = re.exec(source))) {
|
|
279
|
+
const before = source.slice(lastIndex, match.index);
|
|
280
|
+
result += stripLegacyDocdexBodySegment(before, body);
|
|
281
|
+
result += match[0];
|
|
282
|
+
lastIndex = match.index + match[0].length;
|
|
283
|
+
}
|
|
284
|
+
result += stripLegacyDocdexBodySegment(source.slice(lastIndex), body);
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function mergeInstructionText(existing, instructions, { prepend = false } = {}) {
|
|
176
289
|
const next = normalizeInstructionText(instructions);
|
|
177
290
|
if (!next) return normalizeInstructionText(existing);
|
|
178
|
-
const
|
|
291
|
+
const existingText = String(existing || "");
|
|
292
|
+
const current = normalizeInstructionText(existingText);
|
|
179
293
|
if (!current) return next;
|
|
180
|
-
|
|
181
|
-
|
|
294
|
+
const version = extractDocdexBlockVersion(next);
|
|
295
|
+
if (version) {
|
|
296
|
+
const body = extractDocdexBlockBody(next);
|
|
297
|
+
const cleaned = stripLegacyDocdexBody(existingText, body);
|
|
298
|
+
const withoutOldBlocks = stripDocdexBlocksExcept(cleaned, version);
|
|
299
|
+
if (hasDocdexBlockVersion(withoutOldBlocks, version)) return withoutOldBlocks;
|
|
300
|
+
const remainder = normalizeInstructionText(stripDocdexBlocks(withoutOldBlocks));
|
|
301
|
+
if (!remainder) return next;
|
|
302
|
+
return prepend ? `${next}\n\n${remainder}` : `${remainder}\n\n${next}`;
|
|
303
|
+
}
|
|
304
|
+
if (existingText.includes(next)) return existingText;
|
|
305
|
+
return prepend ? `${next}\n\n${current}` : `${current}\n\n${next}`;
|
|
182
306
|
}
|
|
183
307
|
|
|
184
308
|
function writeTextFile(pathname, contents) {
|
|
@@ -199,13 +323,10 @@ function upsertPromptFile(pathname, instructions, { prepend = false } = {}) {
|
|
|
199
323
|
let current = "";
|
|
200
324
|
if (fs.existsSync(pathname)) {
|
|
201
325
|
current = fs.readFileSync(pathname, "utf8");
|
|
202
|
-
if (current.includes(next)) return false;
|
|
203
|
-
}
|
|
204
|
-
const currentTrimmed = normalizeInstructionText(current);
|
|
205
|
-
let merged = next;
|
|
206
|
-
if (currentTrimmed) {
|
|
207
|
-
merged = prepend ? `${next}\n\n${currentTrimmed}` : `${currentTrimmed}\n\n${next}`;
|
|
208
326
|
}
|
|
327
|
+
const merged = mergeInstructionText(current, instructions, { prepend });
|
|
328
|
+
if (!merged) return false;
|
|
329
|
+
if (merged === current) return false;
|
|
209
330
|
return writeTextFile(pathname, merged);
|
|
210
331
|
}
|
|
211
332
|
|
|
@@ -220,13 +341,51 @@ function upsertYamlInstruction(pathname, key, instructions) {
|
|
|
220
341
|
if (fs.existsSync(pathname)) {
|
|
221
342
|
current = fs.readFileSync(pathname, "utf8");
|
|
222
343
|
}
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
344
|
+
const lines = current.split(/\r?\n/);
|
|
345
|
+
const blockRe = new RegExp(`^(\\s*)${escapeRegExp(key)}\\s*:\\s*(\\|[+-]?)?\\s*$`);
|
|
346
|
+
for (let idx = 0; idx < lines.length; idx += 1) {
|
|
347
|
+
const match = lines[idx].match(blockRe);
|
|
348
|
+
if (!match) continue;
|
|
349
|
+
const indent = match[1] || "";
|
|
350
|
+
const blockIndent = `${indent} `;
|
|
351
|
+
let existingBlock = "";
|
|
352
|
+
let blockEnd = idx + 1;
|
|
353
|
+
if (match[2]) {
|
|
354
|
+
for (let j = idx + 1; j < lines.length; j += 1) {
|
|
355
|
+
const line = lines[j];
|
|
356
|
+
if (!line.trim()) {
|
|
357
|
+
blockEnd = j + 1;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const leading = line.match(/^\s*/)[0].length;
|
|
361
|
+
if (leading <= indent.length) break;
|
|
362
|
+
blockEnd = j + 1;
|
|
363
|
+
}
|
|
364
|
+
const blockLines = lines.slice(idx + 1, blockEnd);
|
|
365
|
+
existingBlock = blockLines
|
|
366
|
+
.map((line) => {
|
|
367
|
+
if (!line.trim()) return "";
|
|
368
|
+
return line.startsWith(blockIndent) ? line.slice(blockIndent.length) : line.trimStart();
|
|
369
|
+
})
|
|
370
|
+
.join("\n");
|
|
371
|
+
} else {
|
|
372
|
+
const inlineMatch = lines[idx].match(new RegExp(`^(\\s*)${escapeRegExp(key)}\\s*:\\s*(.*)$`));
|
|
373
|
+
existingBlock = inlineMatch ? inlineMatch[2].trim() : "";
|
|
374
|
+
}
|
|
375
|
+
const merged = mergeInstructionText(existingBlock, instructions);
|
|
376
|
+
if (!merged) return false;
|
|
377
|
+
if (normalizeInstructionText(merged) === normalizeInstructionText(existingBlock) && match[2]) return false;
|
|
378
|
+
const mergedLines = merged.split(/\r?\n/).map((line) => `${blockIndent}${line}`);
|
|
379
|
+
const updatedLines = [
|
|
380
|
+
...lines.slice(0, idx),
|
|
381
|
+
`${indent}${key}: |`,
|
|
382
|
+
...mergedLines,
|
|
383
|
+
...lines.slice(match[2] ? blockEnd : idx + 1)
|
|
384
|
+
];
|
|
385
|
+
return writeTextFile(pathname, updatedLines.join("\n").trimEnd());
|
|
227
386
|
}
|
|
228
|
-
const
|
|
229
|
-
const block = `${key}: |\n${
|
|
387
|
+
const contentLines = next.split(/\r?\n/).map((line) => ` ${line}`);
|
|
388
|
+
const block = `${key}: |\n${contentLines.join("\n")}`;
|
|
230
389
|
const merged = current.trim() ? `${current.trim()}\n\n${block}` : block;
|
|
231
390
|
return writeTextFile(pathname, merged);
|
|
232
391
|
}
|
|
@@ -668,7 +827,7 @@ function resolveBinaryPath({ binaryPath } = {}) {
|
|
|
668
827
|
}
|
|
669
828
|
|
|
670
829
|
function applyAgentInstructions({ logger } = {}) {
|
|
671
|
-
const instructions = loadAgentInstructions();
|
|
830
|
+
const instructions = buildDocdexInstructionBlock(loadAgentInstructions());
|
|
672
831
|
if (!normalizeInstructionText(instructions)) return { ok: false, reason: "missing_instructions" };
|
|
673
832
|
const paths = clientInstructionPaths();
|
|
674
833
|
let updated = false;
|