vault backup: 2026-02-24 07:55:02

This commit is contained in:
windyboy
2026-02-24 07:55:02 +08:00
parent 37b29f36b3
commit 68182b70fd
37 changed files with 13503 additions and 6802 deletions
+7
View File
@@ -0,0 +1,7 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
*No recent activity*
</claude-mem-context>
+77
View File
@@ -0,0 +1,77 @@
{
"userName": "windy",
"enableBlocklist": true,
"blockedCommands": {
"unix": [
"rm -rf",
"chmod 777",
"chmod -R 777"
],
"windows": [
"del /s /q",
"rd /s /q",
"rmdir /s /q",
"format",
"diskpart",
"Remove-Item -Recurse -Force",
"Remove-Item -Force -Recurse",
"Remove-Item -r -fo",
"Remove-Item -fo -r",
"Remove-Item -Recurse",
"Remove-Item -r",
"ri -Recurse",
"ri -r",
"ri -Force",
"ri -fo",
"rm -r -fo",
"rm -Recurse",
"rm -Force",
"del -Recurse",
"del -Force",
"erase -Recurse",
"erase -Force",
"rd -Recurse",
"rmdir -Recurse",
"Format-Volume",
"Clear-Disk",
"Initialize-Disk",
"Remove-Partition"
]
},
"permissionMode": "normal",
"model": "sonnet",
"thinkingBudget": "low",
"enableAutoTitleGeneration": true,
"titleGenerationModel": "",
"show1MModel": false,
"enableChrome": false,
"excludedTags": [],
"mediaFolder": "05_Attachments",
"systemPrompt": "",
"allowedExportPaths": [
"~/Downloads"
],
"persistentExternalContextPaths": [],
"environmentVariables": "",
"envSnippets": [],
"customContextLimits": {},
"keyboardNavigation": {
"scrollUpKey": "w",
"scrollDownKey": "s",
"focusInputKey": "i"
},
"locale": "en",
"claudeCliPath": "",
"claudeCliPathsByHost": {
"windy-pc": "C:\\Users\\windy\\.local\\bin\\claude.exe"
},
"loadUserClaudeSettings": true,
"lastClaudeModel": "sonnet",
"lastCustomModel": "",
"lastEnvHash": "",
"maxTabs": 3,
"tabBarPosition": "input",
"enableAutoScroll": true,
"openInMainTab": false,
"hiddenSlashCommands": []
}
+8
View File
@@ -0,0 +1,8 @@
personality = "pragmatic"
[mcp_servers.qmd]
command = "bun"
args = ['C:\Users\windy\.bun\install\global\node_modules\qmd\src\qmd.ts', "mcp"]
[mcp_servers.qmd.env]
INDEX_PATH = 'C:\Users\windy\.qmd\index.sqlite'
+7
View File
@@ -0,0 +1,7 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
*No recent activity*
</claude-mem-context>
+2 -2
View File
@@ -12,7 +12,6 @@
"obsidian-kanban",
"obsidian-icon-folder",
"highlightr-plugin",
"obsidian-git",
"obsidian-excalidraw-plugin",
"code-styler",
"note-buddy",
@@ -21,5 +20,6 @@
"better-word-count",
"table-editor-obsidian",
"obsidian-advanced-uri",
"advanced-canvas"
"advanced-canvas",
"obsidian-git"
]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "advanced-canvas",
"name": "Advanced Canvas",
"version": "5.6.5",
"version": "6.0.0",
"minAppVersion": "1.1.0",
"description": "Supercharge your canvas experience! Create presentations, flowcharts and more!",
"author": "Developer-Mike",
+6 -6
View File
@@ -10,9 +10,6 @@
width: 100%;
border-radius: 0 var(--input-radius) var(--input-radius) 0;
}
.ac-settings-heading {
border-bottom: 1px solid var(--color-accent);
}
.ac-settings-heading:not(:first-child) {
margin-top: var(--size-4-10) !important;
}
@@ -154,7 +151,6 @@ details.setting-item > *:not(summary) {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 0 !important;
text-align: center;
vertical-align: middle;
}
@@ -392,6 +388,10 @@ details.setting-item > *:not(summary) {
.canvas-wrapper[data-disable-font-size-relative-to-zoom=true] {
--zoom-multiplier: 1 !important;
}
.canvas-wrapper.mod-readonly .canvas-controls .canvas-control-group:nth-child(3),
.canvas-wrapper.mod-readonly .canvas-card-menu {
display: none;
}
.canvas-wrapper.mod-readonly[data-hide-background-grid-when-in-readonly=true] .canvas-background {
visibility: hidden;
}
@@ -457,10 +457,10 @@ details.setting-item > *:not(summary) {
.canvas-wrapper[data-focus-mode-enabled=true] .canvas:has(.canvas-node.is-focused) .canvas-node:not(.is-focused) {
filter: blur(5px);
}
.canvas-wrapper[data-focus-mode-enabled=true] .canvas:has(.canvas-node.is-focused) .canvas-edges {
.canvas-wrapper[data-focus-mode-enabled=true] .canvas:has(.canvas-node.is-focused) .canvas-edges > g:not(.is-focused) {
filter: blur(5px);
}
.canvas-wrapper[data-focus-mode-enabled=true] .canvas:has(.canvas-node.is-focused) .canvas-path-label-wrapper {
.canvas-wrapper[data-focus-mode-enabled=true] .canvas:has(.canvas-node.is-focused) .canvas-path-label-wrapper > .canvas-path-label:not(.is-focused) {
filter: blur(5px);
}
.canvas-wrapper.presentation-mode .canvas-controls {
+7
View File
@@ -0,0 +1,7 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
*No recent activity*
</claude-mem-context>
+8980 -4188
View File
File diff suppressed because it is too large Load Diff
+1067 -253
View File
File diff suppressed because it is too large Load Diff
+1545 -1686
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "copilot",
"name": "Copilot",
"version": "3.1.5",
"version": "3.2.2",
"minAppVersion": "0.15.0",
"description": "Your AI Copilot: Chat with Your Second Brain, Learn Faster, Work Smarter.",
"author": "Logan Yang",
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
*No recent activity*
</claude-mem-context>
+2 -1
View File
@@ -42,6 +42,7 @@
"isLeftHanded": false,
"desktopUIMode": "tray",
"tabletUIMode": "compact",
"phoneUIMode": "mobile",
"iframeMatchExcalidrawTheme": true,
"matchTheme": false,
"matchThemeAlways": false,
@@ -123,7 +124,7 @@
"mdBorderColor": "Black",
"mdCSS": "",
"scriptEngineSettings": {},
"previousRelease": "2.20.0",
"previousRelease": "2.20.5",
"showReleaseNotes": true,
"compareManifestToPluginVersion": true,
"showNewVersionNotification": true,
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.20.0",
"version": "2.20.5",
"minAppVersion": "1.5.7",
"description": "Sketch Your Mind. An Obsidian plugin to edit and view Excalidraw drawings. Enter the world of 4D Visual PKM.",
"author": "Zsolt Viczian",
File diff suppressed because one or more lines are too long
+281 -271
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -6,5 +6,5 @@
"description": "Integrate Git version control with automatic backup and other advanced features.",
"isDesktopOnly": false,
"fundingUrl": "https://ko-fi.com/vinzent",
"version": "2.36.1"
"version": "2.37.1"
}
+157 -42
View File
@@ -56299,7 +56299,11 @@ var require_map2 = __commonJS({
let endMarker = 0;
marked.walkTokens(tokens, (token) => {
const blockReferenceRegex = /[^\S\r\n]*\^([a-zA-Z0-9_-]+)\s*$/;
startContent = document2.indexOf(token.raw, startContent);
const found = document2.indexOf(token.raw, startContent);
if (found === -1) {
return;
}
startContent = found;
const match = blockReferenceRegex.exec(token.raw);
endContent = startContent + (match ? match.index : token.raw.length);
const startMarker = match ? startContent + match.index : -1;
@@ -56334,10 +56338,16 @@ var require_map2 = __commonJS({
};
}
if (constants_js_1.TARGETABLE_BY_ISOLATED_BLOCK_REFERENCE.includes(token.type)) {
let adjustedEndContent = endContent;
if (document2.slice(adjustedEndContent - 1, adjustedEndContent) !== "\n" && document2.slice(adjustedEndContent, adjustedEndContent + 1) === "\n") {
adjustedEndContent += 1;
} else if (document2.slice(adjustedEndContent - 2, adjustedEndContent) !== "\r\n" && document2.slice(adjustedEndContent, adjustedEndContent + 2) === "\r\n") {
adjustedEndContent += 2;
}
lastBlockDetails = {
token,
start: startContent,
end: endContent - 1
end: adjustedEndContent - 1
};
}
});
@@ -56501,10 +56511,14 @@ var require_patch = __commonJS({
};
exports.MergeNotPossible = MergeNotPossible;
var replaceText = (document2, instruction, target) => {
const suffix = document2.slice(target.content.end);
const lineEnding = suffix.startsWith("\r\n") ? "\r\n" : "\n";
const hasSingleLeadingNewline = suffix.startsWith(lineEnding) && !suffix.startsWith(lineEnding + lineEnding);
const content = hasSingleLeadingNewline && typeof instruction.content === "string" && !instruction.content.endsWith("\n") && !instruction.content.endsWith("\r\n") ? instruction.content + lineEnding : instruction.content;
return [
document2.slice(0, target.content.start),
instruction.content,
document2.slice(target.content.end)
content,
suffix
].join("");
};
var prependText = (document2, instruction, target) => {
@@ -56930,15 +56944,18 @@ function getSplicePosition(fileLines, heading, insert, ignoreNewLines) {
return splicePosition;
}
function toArrayBuffer(arr) {
if (arr instanceof Uint8Array) {
return arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength);
}
if (arr instanceof DataView) {
return arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength);
}
if (arr instanceof ArrayBuffer) {
return arr;
}
if (arr instanceof Uint8Array || arr instanceof DataView) {
const view = arr instanceof Uint8Array ? arr : new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
if (view.buffer instanceof ArrayBuffer) {
return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
}
const copy = new Uint8Array(view.byteLength);
copy.set(view);
return copy.buffer;
}
const encoder = new TextEncoder();
return encoder.encode(JSON.stringify(arr)).buffer;
}
@@ -57110,13 +57127,47 @@ var RequestHandler = class {
};
});
}
waitForFileCache(file, timeoutMs = 5e3) {
const existingCache = this.app.metadataCache.getFileCache(file);
if (existingCache) {
return Promise.resolve(existingCache);
}
return new Promise((resolve) => {
let resolved = false;
const onCacheChange = (...data) => {
const changedFile = data[0];
if (changedFile.path === file.path && !resolved) {
resolved = true;
this.app.metadataCache.off("changed", onCacheChange);
clearTimeout(timeoutId);
resolve(this.app.metadataCache.getFileCache(file));
}
};
const timeoutId = setTimeout(() => {
if (!resolved) {
resolved = true;
this.app.metadataCache.off("changed", onCacheChange);
console.warn(`[REST API] Timeout waiting for metadata cache for ${file.path} after ${timeoutMs}ms`);
resolve(this.app.metadataCache.getFileCache(file));
}
}, timeoutMs);
this.app.metadataCache.on("changed", onCacheChange);
const cacheAfterListener = this.app.metadataCache.getFileCache(file);
if (cacheAfterListener && !resolved) {
resolved = true;
this.app.metadataCache.off("changed", onCacheChange);
clearTimeout(timeoutId);
resolve(cacheAfterListener);
}
});
}
getFileMetadataObject(file) {
return __async(this, null, function* () {
var _a2, _b, _c;
const cache = this.app.metadataCache.getFileCache(file);
const frontmatter = __spreadValues({}, (_a2 = cache.frontmatter) != null ? _a2 : {});
var _a2, _b;
const cache = yield this.waitForFileCache(file);
const frontmatter = __spreadValues({}, (_a2 = cache == null ? void 0 : cache.frontmatter) != null ? _a2 : {});
delete frontmatter.position;
const directTags = (_c = ((_b = cache.tags) != null ? _b : []).filter((tag) => tag).map((tag) => tag.tag)) != null ? _c : [];
const directTags = ((_b = cache == null ? void 0 : cache.tags) != null ? _b : []).filter((tag) => tag).map((tag) => tag.tag);
const frontmatterTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
const filteredTags = [...frontmatterTags, ...directTags].filter((tag) => tag).map((tag) => tag.toString().replace(/^#/, "")).filter((value, index, self2) => self2.indexOf(value) === index);
return {
@@ -57133,11 +57184,12 @@ var RequestHandler = class {
message,
errorCode
}) {
var _a2;
const errorMessages = [];
if (errorCode) {
errorMessages.push(ERROR_CODE_MESSAGES[errorCode]);
} else {
errorMessages.push(import_http.default.STATUS_CODES[statusCode]);
errorMessages.push((_a2 = import_http.default.STATUS_CODES[statusCode]) != null ? _a2 : "Unknown Error");
}
if (message) {
errorMessages.push(message);
@@ -57147,20 +57199,28 @@ var RequestHandler = class {
getStatusCode({ statusCode, errorCode }) {
if (statusCode) {
return statusCode;
} else if (errorCode) {
return Math.floor(errorCode / 100);
}
return Math.floor(errorCode / 100);
throw new Error("Either statusCode or errorCode must be provided");
}
returnCannedResponse(res, { statusCode, message, errorCode }) {
if (!statusCode && !errorCode) {
throw new Error("Either statusCode or errorCode must be provided");
}
const response = {
message: this.getResponseMessage({ statusCode, message, errorCode }),
errorCode: errorCode != null ? errorCode : statusCode * 100
errorCode: errorCode != null ? errorCode : (statusCode != null ? statusCode : -1) * 100
};
res.status(this.getStatusCode({ statusCode, errorCode })).json(response);
}
root(req, res) {
var _a2;
let certificate;
try {
certificate = import_node_forge.default.pki.certificateFromPem(this.settings.crypto.cert);
if ((_a2 = this.settings.crypto) == null ? void 0 : _a2.cert) {
certificate = import_node_forge.default.pki.certificateFromPem(this.settings.crypto.cert);
}
} catch (e) {
}
res.status(200).json({
@@ -57201,7 +57261,8 @@ var RequestHandler = class {
});
} else {
const exists = yield this.app.vault.adapter.exists(path2);
if (exists && (yield this.app.vault.adapter.stat(path2)).type === "file") {
const statResult = exists ? yield this.app.vault.adapter.stat(path2) : null;
if (statResult && statResult.type === "file") {
const content = yield this.app.vault.adapter.readBinary(path2);
const mimeType = import_mime_types.default.lookup(path2);
res.set({
@@ -57264,6 +57325,7 @@ var RequestHandler = class {
}
_vaultPatchV2(path2, req, res) {
return __async(this, null, function* () {
var _a2;
const headingBoundary = req.get("Heading-Boundary") || "::";
const heading = (req.get("Heading") || "").split(headingBoundary).filter(Boolean);
const contentPosition = req.get("Content-Insertion-Position");
@@ -57288,7 +57350,7 @@ var RequestHandler = class {
return;
}
if (typeof req.get("Content-Insertion-Ignore-Newline") == "string") {
aboveNewLine = req.get("Content-Insertion-Ignore-Newline").toLowerCase() == "true";
aboveNewLine = ((_a2 = req.get("Content-Insertion-Ignore-Newline")) == null ? void 0 : _a2.toLowerCase()) == "true";
}
if (!heading.length) {
this.returnCannedResponse(res, {
@@ -57304,6 +57366,9 @@ var RequestHandler = class {
return;
}
const cache = this.app.metadataCache.getFileCache(file);
if (!cache) {
throw new Error("Error awaiting metadata cache for file in _vaultPatchV2: cache is null");
}
const position = findHeadingBoundary(cache, heading);
if (!position) {
this.returnCannedResponse(res, {
@@ -57323,9 +57388,10 @@ var RequestHandler = class {
}
_vaultPatchV3(path2, req, res) {
return __async(this, null, function* () {
var _a2;
const operation = req.get("Operation");
const targetType = req.get("Target-Type");
const rawTarget = decodeURIComponent(req.get("Target"));
const rawTarget = decodeURIComponent((_a2 = req.get("Target")) != null ? _a2 : "");
const contentType = req.get("Content-Type");
const createTargetIfMissing = req.get("Create-Target-If-Missing") == "true";
const applyIfContentPreexists = req.get("Apply-If-Content-Preexists") == "true";
@@ -57532,8 +57598,8 @@ var RequestHandler = class {
}
periodicGetNote(periodName, timestamp) {
const [period, err] = this.periodicGetInterface(periodName);
if (err) {
return [null, err];
if (err || !period) {
return [null, err != null ? err : ErrorCode.PeriodDoesNotExist];
}
const now = window.moment(timestamp);
const all = period.getAll();
@@ -57549,15 +57615,20 @@ var RequestHandler = class {
let file = gottenFile;
if (err === ErrorCode.PeriodicNoteDoesNotExist) {
const [period] = this.periodicGetInterface(periodName);
if (!period) {
return [null, ErrorCode.PeriodDoesNotExist];
}
const now = window.moment(Date.now());
file = yield period.create(now);
const metadataCachePromise = new Promise((resolve) => {
let cache = null;
const interval = setInterval(() => {
cache = this.app.metadataCache.getFileCache(file);
if (cache) {
clearInterval(interval);
resolve(cache);
if (file) {
cache = this.app.metadataCache.getFileCache(file);
if (cache) {
clearInterval(interval);
resolve(cache);
}
}
}, 100);
});
@@ -57576,7 +57647,7 @@ var RequestHandler = class {
getPeriodicDateFromParams(params) {
const { year, month, day } = params;
if (year && month && day) {
const date = new Date(year, month - 1, day);
const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
return date.getTime();
}
return Date.now();
@@ -57585,8 +57656,10 @@ var RequestHandler = class {
return __async(this, null, function* () {
const date = this.getPeriodicDateFromParams(req.params);
const [file, err] = this.periodicGetNote(req.params.period, date);
if (err) {
this.returnCannedResponse(res, { errorCode: err });
if (err || !file) {
this.returnCannedResponse(res, {
errorCode: err != null ? err : ErrorCode.PeriodicNoteDoesNotExist
});
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultGet.bind(this));
@@ -57596,8 +57669,10 @@ var RequestHandler = class {
return __async(this, null, function* () {
const date = this.getPeriodicDateFromParams(req.params);
const [file, err] = yield this.periodicGetOrCreateNote(req.params.period, date);
if (err) {
this.returnCannedResponse(res, { errorCode: err });
if (err || !file) {
this.returnCannedResponse(res, {
errorCode: err != null ? err : ErrorCode.PeriodicNoteDoesNotExist
});
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPut.bind(this));
@@ -57607,8 +57682,10 @@ var RequestHandler = class {
return __async(this, null, function* () {
const date = this.getPeriodicDateFromParams(req.params);
const [file, err] = yield this.periodicGetOrCreateNote(req.params.period, date);
if (err) {
this.returnCannedResponse(res, { errorCode: err });
if (err || !file) {
this.returnCannedResponse(res, {
errorCode: err != null ? err : ErrorCode.PeriodicNoteDoesNotExist
});
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPost.bind(this));
@@ -57618,8 +57695,10 @@ var RequestHandler = class {
return __async(this, null, function* () {
const date = this.getPeriodicDateFromParams(req.params);
const [file, err] = yield this.periodicGetOrCreateNote(req.params.period, date);
if (err) {
this.returnCannedResponse(res, { errorCode: err });
if (err || !file) {
this.returnCannedResponse(res, {
errorCode: err != null ? err : ErrorCode.PeriodicNoteDoesNotExist
});
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPatch.bind(this));
@@ -57629,8 +57708,10 @@ var RequestHandler = class {
return __async(this, null, function* () {
const date = this.getPeriodicDateFromParams(req.params);
const [file, err] = this.periodicGetNote(req.params.period, date);
if (err) {
this.returnCannedResponse(res, { errorCode: err });
if (err || !file) {
this.returnCannedResponse(res, {
errorCode: err != null ? err : ErrorCode.PeriodicNoteDoesNotExist
});
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultDelete.bind(this));
@@ -57639,30 +57720,50 @@ var RequestHandler = class {
activeFileGet(req, res) {
return __async(this, null, function* () {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultGet.bind(this));
});
}
activeFilePut(req, res) {
return __async(this, null, function* () {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPut.bind(this));
});
}
activeFilePost(req, res) {
return __async(this, null, function* () {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPost.bind(this));
});
}
activeFilePatch(req, res) {
return __async(this, null, function* () {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultPatch.bind(this));
});
}
activeFileDelete(req, res) {
return __async(this, null, function* () {
const file = this.app.workspace.getActiveFile();
if (!file) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
return this.redirectToVaultPath(file, req, res, this._vaultDelete.bind(this));
});
}
@@ -57691,7 +57792,11 @@ var RequestHandler = class {
try {
this.app.commands.executeCommandById(req.params.commandId);
} catch (e) {
this.returnCannedResponse(res, { statusCode: 500, message: e.message });
const error = e;
this.returnCannedResponse(res, {
statusCode: 500,
message: error.message
});
return;
}
this.returnCannedResponse(res, { statusCode: 204 });
@@ -57755,7 +57860,10 @@ var RequestHandler = class {
});
}
}
results.sort((a, b2) => a.score > b2.score ? 1 : -1);
results.sort((a, b2) => {
var _a2, _b;
return ((_a2 = a.score) != null ? _a2 : 0) > ((_b = b2.score) != null ? _b : 0) ? 1 : -1;
});
res.json(results);
});
}
@@ -57810,7 +57918,8 @@ var RequestHandler = class {
});
}
} catch (e) {
throw new Error(`${e.message} (while processing ${file.path})`);
const error = e;
throw new Error(`${error.message} (while processing ${file.path})`);
}
}
return results;
@@ -57832,9 +57941,10 @@ var RequestHandler = class {
const results = yield handlers[contentType]();
res.json(results);
} catch (e) {
const error = e;
this.returnCannedResponse(res, {
errorCode: ErrorCode.InvalidFilterQuery,
message: `${e.message}`
message: `${error.message}`
});
return;
}
@@ -57853,6 +57963,11 @@ var RequestHandler = class {
}
certificateGet(req, res) {
return __async(this, null, function* () {
var _a2;
if (!((_a2 = this.settings.crypto) == null ? void 0 : _a2.cert)) {
this.returnCannedResponse(res, { statusCode: 404 });
return;
}
res.set("Content-type", `application/octet-stream; filename="${CERT_NAME}"`);
res.status(200).send(this.settings.crypto.cert);
});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "obsidian-local-rest-api",
"name": "Local REST API",
"version": "3.4.2",
"version": "3.4.4",
"minAppVersion": "0.12.0",
"description": "Get, change or otherwise interact with your notes in Obsidian via a REST API.",
"author": "Adam Coddington",
+1 -1
View File
@@ -6,7 +6,7 @@
"devMode": false,
"templateFolderPath": "06_Metadata/Templates",
"announceUpdates": "all",
"version": "2.10.0",
"version": "2.11.0",
"globalVariables": {},
"onePageInputEnabled": false,
"disableOnlineFeatures": true,
+51 -49
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "quickadd",
"name": "QuickAdd",
"version": "2.10.0",
"version": "2.11.0",
"minAppVersion": "1.11.4",
"description": "Quickly add new pages or content to your vault.",
"author": "Christian B. B. Houmann",
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,533 @@
---
title: "tobi/qmd: mini cli search engine for your docs, knowledge bases, meeting notes, whatever. Tracking current sota approaches while being all local"
source: "https://github.com/tobi/qmd"
author:
- "[[Agents]]"
- "[[dgilperez]]"
published:
created: 2026-02-02
description: "mini cli search engine for your docs, knowledge bases, meeting notes, whatever. Tracking current sota approaches while being all local - tobi/qmd"
tags:
- "clippings"
- "webclipper"
---
> [!info] Source
> URL: https://github.com/tobi/qmd
> Title: tobi/qmd: mini cli search engine for your docs, knowledge bases, meeting notes, whatever. Tracking current sota approaches while being all local
> Clipped:
**[qmd](https://github.com/tobi/qmd)** Public
mini cli search engine for your docs, knowledge bases, meeting notes, whatever. Tracking current sota approaches while being all local
[Open in github.dev](https://github.dev/) [Open in a new github.dev tab](https://github.dev/) [Open in codespace](https://github.com/codespaces/new/tobi/qmd?resume=1)
<table><thead><tr><th colspan="2"><span>Name</span></th><th colspan="1"><span>Name</span></th><th><p><span>Last commit message</span></p></th><th colspan="1"><p><span>Last commit date</span></p></th></tr></thead><tbody><tr><td colspan="3"><p><span><a href="https://github.com/tobi/qmd/commit/47b705409eb1427e574ce82c16e1860b216869ed">fix: BM25 score normalization - use Math.abs instead of Math.max (</a><a href="https://github.com/tobi/qmd/pull/76">#76</a><a href="https://github.com/tobi/qmd/commit/47b705409eb1427e574ce82c16e1860b216869ed">)</a></span></p><p><span><a href="https://github.com/tobi/qmd/commit/47b705409eb1427e574ce82c16e1860b216869ed">47b7054</a> ·</span></p><p><a href="https://github.com/tobi/qmd/commits/main/"><span><span><span>172 Commits</span></span></span></a></p></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/tree/main/finetune">finetune</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/tree/main/finetune">finetune</a></p></td><td></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/tree/main/skills/qmd"><span>skills/</span> <span>qmd</span></a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/tree/main/skills/qmd"><span>skills/</span> <span>qmd</span></a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/f6a987a642fccd2a3c6585811fdf92b6b61be2e2">Add skills.sh integration for AI agent discovery (</a><a href="https://github.com/tobi/qmd/pull/64">#64</a><a href="https://github.com/tobi/qmd/commit/f6a987a642fccd2a3c6585811fdf92b6b61be2e2">)</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/tree/main/src">src</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/tree/main/src">src</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/47b705409eb1427e574ce82c16e1860b216869ed">fix: BM25 score normalization - use Math.abs instead of Math.max (</a><a href="https://github.com/tobi/qmd/pull/76">#76</a><a href="https://github.com/tobi/qmd/commit/47b705409eb1427e574ce82c16e1860b216869ed">)</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/tree/main/test">test</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/tree/main/test">test</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/431f6e505ba2fc53f196da03b0580f3f7be59269">Fix qmd embed crash and resolve all TypeScript errors</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/.gitattributes">.gitattributes</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/.gitattributes">.gitattributes</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/99aee7190387483079358cf50b4cd152e607c2c3">Update get and multi-get commands for virtual paths</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/.gitignore">.gitignore</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/.gitignore">.gitignore</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/533f0eed372f03a07d46569ba3fcbca0b1d8cf4e">docs: add finetune CLAUDE.md and update training workflow</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/CLAUDE.md">CLAUDE.md</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/CLAUDE.md">CLAUDE.md</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/17c201ea8173e90962bfb64c4d3758f48cefb224">fix: correct QMD acronym to Query Markup Documents</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/README.md">README.md</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/README.md">README.md</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/17c201ea8173e90962bfb64c4d3758f48cefb224">fix: correct QMD acronym to Query Markup Documents</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/bun.lock">bun.lock</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/bun.lock">bun.lock</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/c85889df12d59a090e21bbe41a02144a7e251191">fixes</a></p></td><td></td></tr><tr><td colspan="2"><p><a href="https://github.com/tobi/qmd/blob/main/example-index.yml">example-index.yml</a></p></td><td colspan="1"><p><a href="https://github.com/tobi/qmd/blob/main/example-index.yml">example-index.yml</a></p></td><td><p><a href="https://github.com/tobi/qmd/commit/c85889df12d59a090e21bbe41a02144a7e251191">fixes</a></p></td><td></td></tr><tr><td colspan="3"></td></tr></tbody></table>
An on-device search engine for everything you need to remember. Index your markdown notes, meeting transcripts, documentation, and knowledge bases. Search with keywords or natural language. Ideal for your agentic flows.
QMD combines BM25 full-text search, vector semantic search, and LLM re-ranking—all running locally via node-llama-cpp with GGUF models.
## Quick Start
```
# Install globally
bun install -g https://github.com/tobi/qmd
# Create collections for your notes, docs, and meeting transcripts
qmd collection add ~/notes --name notes
qmd collection add ~/Documents/meetings --name meetings
qmd collection add ~/work/docs --name docs
# Add context to help with search results
qmd context add qmd://notes "Personal notes and ideas"
qmd context add qmd://meetings "Meeting transcripts and notes"
qmd context add qmd://docs "Work documentation"
# Generate embeddings for semantic search
qmd embed
# Search across everything
qmd search "project timeline" # Fast keyword search
qmd vsearch "how to deploy" # Semantic search
qmd query "quarterly planning process" # Hybrid + reranking (best quality)
# Get a specific document
qmd get "meetings/2024-01-15.md"
# Get a document by docid (shown in search results)
qmd get "#abc123"
# Get multiple documents by glob pattern
qmd multi-get "journals/2025-05*.md"
# Search within a specific collection
qmd search "API" -c notes
# Export all matches for an agent
qmd search "API" --all --files --min-score 0.3
```
QMD's `--json` and `--files` output formats are designed for agentic workflows:
```
# Get structured results for an LLM
qmd search "authentication" --json -n 10
# List all relevant files above a threshold
qmd query "error handling" --all --files --min-score 0.4
# Retrieve full document content
qmd get "docs/api-reference.md" --full
```
### MCP Server
Although the tool works perfectly fine when you just tell your agent to use it on the command line, it also exposes an MCP (Model Context Protocol) server for tighter integration.
**Tools exposed:**
- `qmd_search` - Fast BM25 keyword search (supports collection filter)
- `qmd_vsearch` - Semantic vector search (supports collection filter)
- `qmd_query` - Hybrid search with reranking (supports collection filter)
- `qmd_get` - Retrieve document by path or docid (with fuzzy matching suggestions)
- `qmd_multi_get` - Retrieve multiple documents by glob pattern, list, or docids
- `qmd_status` - Index health and collection info
**Claude Desktop configuration** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```
{
"mcpServers": {
"qmd": {
"command": "qmd",
"args": ["mcp"]
}
}
}
```
**Claude Code configuration** (`~/.claude/settings.json`):
```
{
"mcpServers": {
"qmd": {
"command": "qmd",
"args": ["mcp"]
}
}
}
```
## Architecture
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ QMD Hybrid Search Pipeline │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐
│ User Query │
└────────┬────────┘
┌──────────────┴──────────────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Query Expansion│ │ Original Query│
│ (fine-tuned) │ │ (×2 weight) │
└───────┬────────┘ └───────┬────────┘
│ │
│ 2 alternative queries │
└──────────────┬──────────────┘
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Original Query │ │ Expanded Query 1│ │ Expanded Query 2│
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
┌───────┴───────┐ ┌───────┴───────┐ ┌───────┴───────┐
▼ ▼ ▼ ▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
│ BM25 │ │Vector │ │ BM25 │ │Vector │ │ BM25 │ │Vector │
│(FTS5) │ │Search │ │(FTS5) │ │Search │ │(FTS5) │ │Search │
└───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘
│ │ │ │ │ │
└───────┬───────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────────────────────┼───────────────────────┘
┌───────────────────────┐
│ RRF Fusion + Bonus │
│ Original query: ×2 │
│ Top-rank bonus: +0.05│
│ Top 30 Kept │
└───────────┬───────────┘
┌───────────────────────┐
│ LLM Re-ranking │
│ (qwen3-reranker) │
│ Yes/No + logprobs │
└───────────┬───────────┘
┌───────────────────────┐
│ Position-Aware Blend │
│ Top 1-3: 75% RRF │
│ Top 4-10: 60% RRF │
│ Top 11+: 40% RRF │
└───────────────────────┘
```
### Search Backends
| Backend | Raw Score | Conversion | Range |
| --- | --- | --- | --- |
| **FTS (BM25)** | SQLite FTS5 BM25 | `Math.abs(score)` | 0 to ~25+ |
| **Vector** | Cosine distance | `1 / (1 + distance)` | 0.0 to 1.0 |
| **Reranker** | LLM 0-10 rating | `score / 10` | 0.0 to 1.0 |
### Fusion Strategy
The `query` command uses **Reciprocal Rank Fusion (RRF)** with position-aware blending:
1. **Query Expansion**: Original query (×2 for weighting) + 1 LLM variation
2. **Parallel Retrieval**: Each query searches both FTS and vector indexes
3. **RRF Fusion**: Combine all result lists using `score = Σ(1/(k+rank+1))` where k=60
4. **Top-Rank Bonus**: Documents ranking #1 in any list get +0.05, #2-3 get +0.02
5. **Top-K Selection**: Take top 30 candidates for reranking
6. **Re-ranking**: LLM scores each document (yes/no with logprobs confidence)
7. **Position-Aware Blending**:
- RRF rank 1-3: 75% retrieval, 25% reranker (preserves exact matches)
- RRF rank 4-10: 60% retrieval, 40% reranker
- RRF rank 11+: 40% retrieval, 60% reranker (trust reranker more)
**Why this approach**: Pure RRF can dilute exact matches when expanded queries don't match. The top-rank bonus preserves documents that score #1 for the original query. Position-aware blending prevents the reranker from destroying high-confidence retrieval results.
### Score Interpretation
| Score | Meaning |
| --- | --- |
| 0.8 - 1.0 | Highly relevant |
| 0.5 - 0.8 | Moderately relevant |
| 0.2 - 0.5 | Somewhat relevant |
| 0.0 - 0.2 | Low relevance |
## Requirements
### System Requirements
- **Bun** >= 1.0.0
- **macOS**: Homebrew SQLite (for extension support)
```
brew install sqlite
```
QMD uses three local GGUF models (auto-downloaded on first use):
| Model | Purpose | Size |
| --- | --- | --- |
| `embeddinggemma-300M-Q8_0` | Vector embeddings | ~300MB |
| `qwen3-reranker-0.6b-q8_0` | Re-ranking | ~640MB |
| `qmd-query-expansion-1.7B-q4_k_m` | Query expansion (fine-tuned) | ~1.1GB |
Models are downloaded from HuggingFace and cached in `~/.cache/qmd/models/`.
## Installation
```
bun install -g github:tobi/qmd
```
Make sure `~/.bun/bin` is in your PATH.
### Development
```
git clone https://github.com/tobi/qmd
cd qmd
bun install
bun link
```
## Usage
### Collection Management
```
# Create a collection from current directory
qmd collection add . --name myproject
# Create a collection with explicit path and custom glob mask
qmd collection add ~/Documents/notes --name notes --mask "**/*.md"
# List all collections
qmd collection list
# Remove a collection
qmd collection remove myproject
# Rename a collection
qmd collection rename myproject my-project
# List files in a collection
qmd ls notes
qmd ls notes/subfolder
```
```
# Embed all indexed documents (800 tokens/chunk, 15% overlap)
qmd embed
# Force re-embed everything
qmd embed -f
```
### Context Management
Context adds descriptive metadata to collections and paths, helping search understand your content.
### Search Commands
```
┌──────────────────────────────────────────────────────────────────┐
│ Search Modes │
├──────────┬───────────────────────────────────────────────────────┤
│ search │ BM25 full-text search only │
│ vsearch │ Vector semantic search only │
│ query │ Hybrid: FTS + Vector + Query Expansion + Re-ranking │
└──────────┴───────────────────────────────────────────────────────┘
```
### Options
```
# Search options
-n <num> # Number of results (default: 5, or 20 for --files/--json)
-c, --collection # Restrict search to a specific collection
--all # Return all matches (use with --min-score to filter)
--min-score <num> # Minimum score threshold (default: 0)
--full # Show full document content
--line-numbers # Add line numbers to output
--index <name> # Use named index
# Output formats (for search and multi-get)
--files # Output: docid,score,filepath,context
--json # JSON output with snippets
--csv # CSV output
--md # Markdown output
--xml # XML output
# Get options
qmd get <file>[:line] # Get document, optionally starting at line
-l <num> # Maximum lines to return
--from <num> # Start from line number
# Multi-get options
-l <num> # Maximum lines per file
--max-bytes <num> # Skip files larger than N bytes (default: 10KB)
```
### Output Format
Default output is colorized CLI format (respects `NO_COLOR` env):
```
docs/guide.md:42 #a1b2c3
Title: Software Craftsmanship
Context: Work documentation
Score: 93%
This section covers the **craftsmanship** of building
quality software with attention to detail.
See also: engineering principles
notes/meeting.md:15 #d4e5f6
Title: Q4 Planning
Context: Personal notes and ideas
Score: 67%
Discussion about code quality and craftsmanship
in the development process.
```
- **Path**: Collection-relative path (e.g., `docs/guide.md`)
- **Docid**: Short hash identifier (e.g., `#a1b2c3`) - use with `qmd get #a1b2c3`
- **Title**: Extracted from document (first heading or filename)
- **Context**: Path context if configured via `qmd context add`
- **Score**: Color-coded (green >70%, yellow >40%, dim otherwise)
- **Snippet**: Context around match with query terms highlighted
### Examples
```
# Get 10 results with minimum score 0.3
qmd query -n 10 --min-score 0.3 "API design patterns"
# Output as markdown for LLM context
qmd search --md --full "error handling"
# JSON output for scripting
qmd query --json "quarterly reports"
# Use separate index for different knowledge base
qmd --index work search "quarterly reports"
```
### Index Maintenance
```
# Show index status and collections with contexts
qmd status
# Re-index all collections
qmd update
# Re-index with git pull first (for remote repos)
qmd update --pull
# Get document by filepath (with fuzzy matching suggestions)
qmd get notes/meeting.md
# Get document by docid (from search results)
qmd get "#abc123"
# Get document starting at line 50, max 100 lines
qmd get notes/meeting.md:50 -l 100
# Get multiple documents by glob pattern
qmd multi-get "journals/2025-05*.md"
# Get multiple documents by comma-separated list (supports docids)
qmd multi-get "doc1.md, doc2.md, #abc123"
# Limit multi-get to files under 20KB
qmd multi-get "docs/*.md" --max-bytes 20480
# Output multi-get as JSON for agent processing
qmd multi-get "docs/*.md" --json
# Clean up cache and orphaned data
qmd cleanup
```
## Data Storage
Index stored in: `~/.cache/qmd/index.sqlite`
### Schema
## Environment Variables
| Variable | Default | Description |
| --- | --- | --- |
| `XDG_CACHE_HOME` | `~/.cache` | Cache directory location |
### Indexing Flow
```
Collection ──► Glob Pattern ──► Markdown Files ──► Parse Title ──► Hash Content
│ │ │
│ │ ▼
│ │ Generate docid
│ │ (6-char hash)
│ │ │
└──────────────────────────────────────────────────►└──► Store in SQLite
FTS5 Index
```
### Embedding Flow
Documents are chunked into 800-token pieces with 15% overlap:
```
Document ──► Chunk (800 tokens) ──► Format each chunk ──► node-llama-cpp ──► Store Vectors
│ "title | text" embedBatch()
└─► Chunks stored with:
- hash: document hash
- seq: chunk sequence (0, 1, 2...)
- pos: character position in original
```
```
Query ──► LLM Expansion ──► [Original, Variant 1, Variant 2]
┌─────────┴─────────┐
▼ ▼
For each query: FTS (BM25)
│ │
▼ ▼
Vector Search Ranked List
Ranked List
└─────────┬─────────┘
RRF Fusion (k=60)
Original query ×2 weight
Top-rank bonus: +0.05/#1, +0.02/#2-3
Top 30 candidates
LLM Re-ranking
(yes/no + logprob confidence)
Position-Aware Blend
Rank 1-3: 75% RRF / 25% reranker
Rank 4-10: 60% RRF / 40% reranker
Rank 11+: 40% RRF / 60% reranker
Final Results
```
## Model Configuration
Models are configured in `src/llm.ts` as HuggingFace URIs:
```
const DEFAULT_EMBED_MODEL = "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
const DEFAULT_RERANK_MODEL = "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf";
const DEFAULT_GENERATE_MODEL = "hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf";
```
```
// For queries
"task: search result | query: {query}"
// For documents
"title: {title} | text: {content}"
```
### Qwen3-Reranker
Uses node-llama-cpp's `createRankingContext()` and `rankAndSort()` API for cross-encoder reranking. Returns documents sorted by relevance score (0.0 - 1.0).
Used for generating query variations via `LlamaChatSession`.
## License
MIT
## Releases
No releases published
## Packages
No packages published
## Languages
- [TypeScript 65.8%](https://github.com/tobi/qmd/search?l=typescript)
- [Python 33.5%](https://github.com/tobi/qmd/search?l=python)
- Other 0.7%
+110
View File
@@ -0,0 +1,110 @@
# 改进计划(基于评审意见,排除 CLAUDE-BOOTSTRAP.md 相关问题)
**计划日期**: 2026-02-03
**适用范围**: README、QUICK_REFERENCE、WORKFLOWS、WEEKLY_REVIEW、package.json、06_Metadata 模板与附件管理文档
**目标**: 统一入口与命令规范,明确工具调用方式,强化文档主线导航与可追溯性。
---
## 阶段 0:基线校准(1 天)
**目标**: 修正评审报告中的硬性数据与证据链。
- 更新 `06_Metadata/REVIEW_REPORT_2026-02-03.md` 的“索引概况”为实际值(当前索引为 639 个 Markdown 文档)。
- 在“关键问题与风险”每条后追加“证据”行(文件路径 + 简短说明)。
- 在“结论”中注明评审日期与索引日期一致。
**验收标准**:
- 报告中所有关键问题具备可追溯证据。
---
## 阶段 1:入口统一(1-2 天)
**目标**: 明确权威入口,消除“并行主线”的认知冲突。
- 在 `README.md` 增加“权威入口”段落,明确:
- `README.md` = 项目入口与导航
- `QUICK_REFERENCE.md` = 速查
- `06_Metadata/WORKFLOWS.md` = 详细工作流
- 在 `QUICK_REFERENCE.md` 顶部增加“入口说明 + 返回导航”。
- 在 `06_Metadata/WORKFLOWS.md` 顶部增加“与 README/Quick Reference 的关系说明”。
**验收标准**:
- 三者之间互相可达,且入口职责清晰一致。
---
## 阶段 2:命令规范一致性(2-3 天)
**目标**: 统一文档命令示例与脚本规则的边界。
- 明确规则(选其一并写入文档):
- 方案 A:文档示例不使用管道、grep、find 等;脚本亦保持简化。
- 方案 B:脚本允许复杂命令,文档中的“手动命令”禁止复杂命令,并明确例外。
- 同步修订 `WEEKLY_REVIEW.md` 的示例命令,使其与规则一致。
- 对 `package.json` 脚本在 README 或 WORKFLOWS 增加说明,避免规则冲突。
**验收标准**:
- 文档中的命令示例与规则完全一致。
- 不再出现“禁止管道但示例有管道”的矛盾。
---
## 阶段 3:工具调用统一(1 天)
**目标**: 统一 `pnpm`/`npm` 的使用方式。
- 统一所有文档的脚本调用为 `pnpm`
- 若保留兼容说明:增加一段“未安装 pnpm 可用 npm run”的规范化说明。
- 修订 README 中出现的 `npm run` 示例,保持一致。
**验收标准**:
- 文档中的脚本调用方式保持一致。
---
## 阶段 4:文档主线导航(1 天)
**目标**: 提供清晰的阅读与执行路径。
- 在 `README.md` 增加“新用户路径”和“维护者路径”。
- 在 `QUICK_REFERENCE.md` 增加“常用路径导航”。
**验收标准**:
- 新用户可以在 2-3 次跳转内找到完整工作流说明。
---
## 阶段 5:复审与维护(0.5-1 天)
**目标**: 验证改动有效、评分依据可复用。
- 对 README、QUICK_REFERENCE、WORKFLOWS、WEEKLY_REVIEW、package.json 做一次一致性复查。
- 更新评审报告评分依据(补充评分标准或示例)。
**验收标准**:
- 评审报告可被他人复核。
---
## 执行顺序建议
1. 阶段 0(校准)
2. 阶段 1(入口)
3. 阶段 2(命令规范)
4. 阶段 3(工具统一)
5. 阶段 4(导航)
6. 阶段 5(复审)
---
## 产出文件清单
- `06_Metadata/IMPROVEMENT_PLAN_2026-02-03.md`(本计划)
- 更新:`06_Metadata/REVIEW_REPORT_2026-02-03.md`
- 更新:`README.md`
- 更新:`QUICK_REFERENCE.md`
- 更新:`06_Metadata/WORKFLOWS.md`
- 更新:`WEEKLY_REVIEW.md`
+27
View File
@@ -0,0 +1,27 @@
# {{date:YYYY-MM-DD}}
## Capture
<!-- Quick thoughts, links, ideas throughout the day -->
获取所有文件名和大小
```
Get-ChildItem -Recurse -File | Select-Object DirectoryName, Name, @{N='Size(MB)';E={"{0:N2}" -f ($_.Length/1MB)}}, LastWriteTime | Export-Csv -Path "all_files_list.csv" -NoTypeInformation -Encoding UTF8
```
## Questions
<!-- What am I curious about today? -->
-
## Insights
<!-- What did I learn or realize? -->
-
## Connections
<!-- Links to other notes or ideas -->
-
## For Tomorrow
<!-- What needs follow-up? -->
-
---
*End of day: Ask Claude Code to review and find connections*
+106
View File
@@ -0,0 +1,106 @@
# Obsidian 仓库评审报告(QMD 驱动)
**评审日期**: 2026-02-03
**评审范围**: 结构、文档、工作流、模板、脚本、维护一致性
**评审方法**: 使用 QMD 索引与检索,对核心说明文档与工作流进行抽样评审
**索引概况**: 639 个 Markdown 文档(QMD index
---
## 总体评分
**综合得分: 86 / 100(成熟度:可持续运作,局部一致性需收敛)**
### 评分维度
1. 信息架构与 PARA 落地(18/20)
2. 文档清晰度与可执行性(16/20)
3. 工作流设计与可持续性(16/20)
4. 模板与元数据规范(14/15)
5. 自动化与脚本可用性(11/15)
6. 一致性与去重(11/15
**加权说明**: 结构与可执行性优先,其次是自动化与一致性。
---
## 主要优点
1. **结构清晰且 PARA 有完整说明**
README 对 PARA 与文件结构的解释完整,且与 `06_Metadata/WORKFLOWS.md` 形成“总览 + 细节”分层。
核心目录(`00_Inbox` ~ `06_Metadata`)清晰,迁移与归档路径明确。
2. **工作流体系成熟,具备“简化版 + 过渡版 + 全量版”**
`ULTRA_SIMPLE_WORKFLOW.md``WORKFLOW_TRANSITION_GUIDE.md``WEEKLY_REVIEW.md`形成明确层级,能覆盖不同熟练度与时间预算。
3. **元数据与模板库基础扎实**
`06_Metadata/Templates``06_Metadata/WORKFLOWS.md` 给出通用 frontmatter 与模板结构,便于系统化扩展。
4. **附件管理有明确规则与脚本支持**
`05_Attachments/README.md`与脚本(package.json scripts)形成闭环,降低手工成本。
---
## 关键问题与风险
1. **核心文档入口不一致**
`README.md``QUICK_REFERENCE.md``06_Metadata/WORKFLOWS.md`对“入口/主线”的定义不一致,入口职责与跳转路径不明确。
结果:新用户或助手会遇到“入口不清晰”的路径断裂。
**证据**: `README.md` 仅提供概览与分散说明,未明确权威入口;`QUICK_REFERENCE.md` 以速查为主但缺少返回入口说明;`06_Metadata/WORKFLOWS.md` 作为详细参考但未说明与入口文档的关系。
2. **命令规范与示例存在冲突**
文档示例中出现 `| wc -l``grep``find` 等复杂命令,但缺少“脚本允许、手动命令禁止”的清晰边界说明。
结果:指导规则与示例出现冲突,影响一致性与可执行性。
**证据**: `WEEKLY_REVIEW.md``ls 00_Inbox/ | wc -l``QUICK_REFERENCE.md``grep -r``find . -name`
3. **文档层级存在“并行主线”**
`README.md``QUICK_REFERENCE.md``06_Metadata/WORKFLOWS.md`均包含“顶层规范”,缺少明确的主线导航与分工说明。
结果:对于“哪个是主入口”的认知不稳定,容易造成重复维护。
**证据**: `README.md` 提供概览与流程,但未明确入口职责;`QUICK_REFERENCE.md` 自包含速查流程;`06_Metadata/WORKFLOWS.md` 标注“详细参考”但未说明与其他入口的关系。
4. **工具链命令调用不统一(pnpm vs npm)**
README 仍出现 `npm run` 的写法,而 Quick Reference/WORKFLOWS 使用 `pnpm`
结果:新用户可能复制即失败,或以为存在两套脚本策略。
**证据**: `README.md``npm run firecrawl:scrape``npm run firecrawl:batch``06_Metadata/WORKFLOWS.md` 中脚本示例为 `pnpm`
---
## 建议(按优先级)
1. **补齐或明确 `CLAUDE.md` 的权威入口**
选项 A:恢复并维护 `CLAUDE.md` 为唯一入口(推荐)
选项 B:将 `CLAUDE-BOOTSTRAP.md` 重命名为 `CLAUDE.md`,并在 README/Quick Reference 全部指向该文件
2. **统一“命令规范”与脚本现实**
如果希望禁用管道命令:
- 将 `WEEKLY_REVIEW.md` 与脚本中管道命令替换为等价的简单命令
如果仅限制“手动执行”而非脚本:
- 在 `CLAUDE-BOOTSTRAP.md`中明确例外(脚本允许管道,交互指令禁止)
3. **统一工具调用方式**
全部文档中统一为 `pnpm`,或新增一段“若未安装 pnpm 可用 npm run”的注释。
4. **建立“文档主线”导航**
在 README 或 Quick Reference 中增加“权威文档入口”段落,并明确:
- `CLAUDE.md` = 系统主入口
- `QUICK_REFERENCE.md` = 速查
- `WORKFLOWS.md` = 详细参考
---
## 结论
该仓库已经具备**成熟的知识管理与工作流体系**,文档覆盖广、结构清晰、脚本化良好。
当前主要瓶颈是**入口一致性与规则冲突**,解决后可显著提升上手体验与可维护性。
**评审日期与索引日期**: 本报告评审日期为 2026-02-03,索引统计日期与之保持一致。
---
## 评分依据(复核用)
- 结构与文档一致性是否可在 2-3 次跳转内定位主流程
- 命令示例与规则是否一致、是否存在可执行冲突
- 入口文档是否明确分工且互相可达
- 关键流程是否有可追溯证据(文件路径 + 具体示例)
**建议:**优先完成“入口文档一致性”与“命令规则一致性”两件事,可将综合评分提升至 90+。
+27 -17
View File
@@ -6,24 +6,35 @@
**Status**: Reference Documentation
> This is a detailed reference. For core rules, see `.claude/project-instructions.md`
> Relationship: `README.md` is the entry point, `QUICK_REFERENCE.md` is the quick reference, and this document is the detailed workflow guide.
---
## Table of Contents
1. [Output Templates](#output-templates)
2. [PARA Method Explained](#para-method-explained)
3. [Naming Conventions](#naming-conventions)
4. [Inbox Management](#inbox-management)
5. [Attachments Management](#attachments-management)
6. [Web Content Workflow](#web-content-workflow)
7. [Writing Style Guidelines](#writing-style-guidelines)
8. [Git Workflow](#git-workflow)
9. [Daily Workflows](#daily-workflows)
10. [Available Commands](#available-commands)
11. [Best Practices](#best-practices)
12. [Troubleshooting](#troubleshooting)
13. [Memvid Workflows](#memvid-workflows)
1. [Command Policy](#command-policy)
2. [Output Templates](#output-templates)
3. [PARA Method Explained](#para-method-explained)
4. [Naming Conventions](#naming-conventions)
5. [Inbox Management](#inbox-management)
6. [Attachments Management](#attachments-management)
7. [Web Content Workflow](#web-content-workflow)
8. [Writing Style Guidelines](#writing-style-guidelines)
9. [Git Workflow](#git-workflow)
10. [Daily Workflows](#daily-workflows)
11. [Available Commands](#available-commands)
12. [Best Practices](#best-practices)
13. [Troubleshooting](#troubleshooting)
14. [Memvid Workflows](#memvid-workflows)
---
## Command Policy
- Manual commands in documentation use simple, copy-safe commands only
- Avoid pipes, `grep`, `find`, or other complex shell constructs in manual steps
- Complex operations belong in `pnpm` scripts
- If `pnpm` is unavailable, use `npm run <script>` as a fallback
---
@@ -469,7 +480,7 @@ git status # Periodically check changes
```bash
git status # Review all changes
git add . # Stage all changes
git commit -m "vault backup: $(date +%Y-%m-%d\ %H:%M:%S)"
git commit -m "vault backup: YYYY-MM-DD HH:MM:SS"
git push # Sync to remote
```
@@ -565,7 +576,7 @@ ls 00_Inbox/
# 3. Commit changes
git status
git add .
git commit -m "vault backup: $(date)"
git commit -m "vault backup: YYYY-MM-DD HH:MM:SS"
git push
```
@@ -613,7 +624,6 @@ git push # Sync to remote
cd D:\tmp\vault\my-vault # Vault root
ls 00_Inbox/ # List inbox
ls 01_Projects/ # List projects
find . -type d | sort # Map all folders
```
---
@@ -717,7 +727,7 @@ find . -type d | sort # Map all folders
**Lost Files**:
- Check git history: `git log --all --full-history -- <path>`
- Look in Archive folders
- Search by content: use grep or Obsidian search
- Search by content: use Obsidian search
- Check recent commits for moves
---
+21 -8
View File
@@ -10,6 +10,14 @@ tags: [reference, quick-start, cheatsheet]
---
## 入口说明 + 返回导航
- `README.md` = 项目入口与导航
- `QUICK_REFERENCE.md` = 速查
- `06_Metadata/WORKFLOWS.md` = 详细工作流
**返回导航**: `[[README]]`
## 🌅 Daily Workflow
**Morning (Start Session)**:
@@ -22,7 +30,7 @@ git pull # Sync latest changes
```bash
git add . # Stage all changes
git commit -m "vault backup: $(date)"
git commit -m "vault backup: YYYY-MM-DD HH:MM:SS"
git push # Sync to remote
```
@@ -103,12 +111,10 @@ mv "05_Attachments/file.png" "05_Attachments/Organized/ProjectName_Description.p
**Search Content**:
- Obsidian: `Ctrl+Shift+F` (or `Cmd+Shift+F`)
- Command line: `grep -r "keyword" .`
**Find Files**:
- Obsidian: `Ctrl+O` (Quick Switcher)
- Command line: `find . -name "*pattern*"`
---
@@ -160,6 +166,14 @@ ls 01_Projects/ # Review active projects
---
## 🧭 常用路径导航
- 日常使用: `README.md``QUICK_REFERENCE.md`
- 深入规则: `README.md``06_Metadata/WORKFLOWS.md`
- 周检流程: `QUICK_REFERENCE.md``WEEKLY_REVIEW.md`
---
## ⚠️ Common Issues
**Q: Git conflict after pull?**
@@ -174,7 +188,7 @@ git push
**Q: Can't find a file?**
- Check: `00_Inbox/`, `04_Archive/`
- Search: `grep -r "filename" .`
- Search: Obsidian 全局搜索
- Git history: `git log --all --full-history -- <path>`
**Q: Broken links after moving?**
@@ -202,11 +216,10 @@ git push
## 📚 Deep Dive Docs
- [[CLAUDE]] - Full configuration guide
- [[README]] - Project entry point and navigation
- [[QUICK_REFERENCE]] - One-page quick reference
- [[06_Metadata/WORKFLOWS]] - Detailed workflows and rules
- [[WEEKLY_REVIEW]] - Complete review checklist
- [[PARA_METHOD]] - PARA method explained (coming soon)
- [[GIT_WORKFLOW]] - Git workflow details (coming soon)
- [[TROUBLESHOOTING]] - Extended troubleshooting (coming soon)
---
+23 -3
View File
@@ -12,6 +12,12 @@ with Claude Code, enabling you to:
- Maintain version control with Git
- Access your vault from anywhere (including mobile)
## Authoritative Entry Points
- `README.md` = Project entry point and navigation
- `QUICK_REFERENCE.md` = One-page quick reference
- `06_Metadata/WORKFLOWS.md` = Detailed workflows and rules
## Quick Start
### 1. Get the Starter Kit
@@ -239,10 +245,10 @@ research projects, documentation archives, or building a knowledge base.
```bash
# Save a single article
npm run firecrawl:scrape -- "https://example.com/article" "03_Resources/Articles"
pnpm firecrawl:scrape -- "https://example.com/article" "03_Resources/Articles"
# Batch save multiple URLs
npm run firecrawl:batch -- urls.txt "03_Resources/Research"
pnpm firecrawl:batch -- urls.txt "03_Resources/Research"
```
**Getting a Firecrawl API key:**
@@ -263,6 +269,20 @@ Run these with `pnpm`:
- `attachments:orphans` - Find unreferenced attachments
- `vault:stats` - Show vault statistics
If you do not have `pnpm` installed, use `npm run <script>` as a fallback.
## Paths
**New User Path**:
- Start at `README.md`
- Use `QUICK_REFERENCE.md` for daily use
- Read `06_Metadata/WORKFLOWS.md` for full rules and workflows
**Maintainer Path**:
- Review `README.md` entry points and changes
- Validate `QUICK_REFERENCE.md` for daily command accuracy
- Update `06_Metadata/WORKFLOWS.md` for rule changes
## Advanced Setup
### Quick Launch from Anywhere
@@ -360,7 +380,7 @@ You are a research assistant.
### Attachment management
- Run `npm run attachments:create-organized` to set up folders
- Run `pnpm attachments:create-organized` to set up folders
- Use helper scripts to find orphaned files
- Keep attachments under 10MB for Git
+2 -2
View File
@@ -23,10 +23,10 @@
### Step 1: Count Current Inbox Items
```bash
ls 00_Inbox/ | wc -l
ls 00_Inbox/
```
**Current Count**: _____ items (excluding permanent files)
**Current Count**: _____ items (excluding permanent files, count manually)
### Step 2: Quick Delete