dsh-desktop-kit 2.2.1 → 2.7.2
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 +80 -0
- package/README.md +113 -27
- package/client.js +3121 -32
- package/docs/AGENT-HARNESS.md +144 -0
- package/docs/ARCHITECTURE.md +38 -7
- package/docs/COMPATIBILITY.md +4 -2
- package/docs/DEVELOPMENT.md +56 -0
- package/docs/INSTALL.md +20 -11
- package/docs/LAPTOP-INSTALL.md +96 -0
- package/docs/ROADMAP-3.0.md +353 -0
- package/docs/ROADMAP.md +1 -1
- package/docs/SUBAGENTS.md +25 -0
- package/docs/TEST-0.2.2.md +5 -5
- package/docs/TEST-2.3.0.md +49 -0
- package/docs/TEST-2.7.2.md +46 -0
- package/docs/TOKENS.md +2 -2
- package/docs/TROUBLESHOOTING.md +27 -0
- package/docs/UI-QA.md +28 -0
- package/docs/images/peak-toast-peak.png +0 -0
- package/docs/images/peak-toast-soon.png +0 -0
- package/docs/preview/peak-toast.html +155 -0
- package/index.js +18 -1
- package/package.json +26 -6
- package/plugins/dsh-agent-extensions/README.md +99 -0
- package/plugins/dsh-agent-extensions/index.js +167 -0
- package/plugins/dsh-agent-extensions/lib/agents.js +76 -0
- package/plugins/dsh-agent-extensions/lib/checkpoints.js +103 -0
- package/plugins/dsh-agent-extensions/lib/commands.js +255 -0
- package/plugins/dsh-agent-extensions/lib/hooks.js +182 -0
- package/plugins/dsh-agent-extensions/lib/policy.js +84 -0
- package/plugins/dsh-agent-extensions/lib/project.js +144 -0
- package/plugins/dsh-agent-extensions/lib/scaffold.js +151 -0
- package/plugins/dsh-agent-extensions/package.json +9 -0
- package/plugins/dsh-browser-qa/README.md +221 -0
- package/plugins/dsh-browser-qa/cordis.patch.yml +4 -0
- package/plugins/dsh-browser-qa/index.js +165 -0
- package/plugins/dsh-browser-qa/lib/cdp.js +224 -0
- package/plugins/dsh-browser-qa/lib/chrome.js +466 -0
- package/plugins/dsh-browser-qa/lib/pages.js +552 -0
- package/plugins/dsh-browser-qa/lib/qa.js +827 -0
- package/plugins/dsh-browser-qa/lib/ui-gate.js +256 -0
- package/plugins/dsh-browser-qa/package.json +31 -0
- package/plugins/dsh-checkpoints/README.md +22 -0
- package/plugins/dsh-checkpoints/cordis.patch.yml +3 -0
- package/plugins/dsh-checkpoints/index.js +80 -0
- package/plugins/dsh-checkpoints/lib/checkpoints.js +111 -0
- package/plugins/dsh-checkpoints/package.json +25 -0
- package/plugins/dsh-github-review/README.md +131 -0
- package/plugins/dsh-github-review/cordis.patch.yml +3 -0
- package/plugins/dsh-github-review/index.js +536 -0
- package/plugins/dsh-github-review/lib/gh.js +494 -0
- package/plugins/dsh-github-review/lib/normalize.js +662 -0
- package/plugins/dsh-github-review/package.json +28 -0
- package/plugins/dsh-guardrails/README.md +264 -0
- package/plugins/dsh-guardrails/cordis.patch.yml +5 -0
- package/plugins/dsh-guardrails/index.js +389 -0
- package/plugins/dsh-guardrails/lib/hooks.js +262 -0
- package/plugins/dsh-guardrails/lib/modes.js +77 -0
- package/plugins/dsh-guardrails/lib/policy.js +592 -0
- package/plugins/dsh-guardrails/lib/scan.js +451 -0
- package/plugins/dsh-guardrails/lib/secrets.js +274 -0
- package/plugins/dsh-guardrails/package.json +26 -0
- package/plugins/dsh-mcp-manager/README.md +78 -0
- package/plugins/dsh-mcp-manager/client.js +373 -0
- package/plugins/dsh-mcp-manager/index.js +159 -0
- package/plugins/dsh-mcp-manager/lib/api.js +250 -0
- package/plugins/dsh-mcp-manager/lib/client.js +219 -0
- package/plugins/dsh-mcp-manager/lib/config.js +217 -0
- package/plugins/dsh-mcp-manager/lib/manager.js +146 -0
- package/plugins/dsh-mcp-manager/package.json +18 -0
- package/plugins/dsh-mcp-manager/screenshots/modal-390.png +0 -0
- package/plugins/dsh-mcp-manager/screenshots/settings-1440.png +0 -0
- package/plugins/dsh-mcp-manager/screenshots/settings-390.png +0 -0
- package/plugins/dsh-memory-bridge/README.md +161 -0
- package/plugins/dsh-memory-bridge/client.js +2264 -0
- package/plugins/dsh-memory-bridge/cordis.patch.yml +5 -0
- package/plugins/dsh-memory-bridge/docs/images/conflicts.png +0 -0
- package/plugins/dsh-memory-bridge/docs/images/hero.png +0 -0
- package/plugins/dsh-memory-bridge/docs/images/settings-dark.png +0 -0
- package/plugins/dsh-memory-bridge/docs/images/settings-light.png +0 -0
- package/plugins/dsh-memory-bridge/docs/images/settings-mobile.png +0 -0
- package/plugins/dsh-memory-bridge/docs/images/setup.png +0 -0
- package/plugins/dsh-memory-bridge/icon.svg +13 -0
- package/plugins/dsh-memory-bridge/index.js +378 -0
- package/plugins/dsh-memory-bridge/lib/bridge.js +636 -0
- package/plugins/dsh-memory-bridge/lib/github.js +473 -0
- package/plugins/dsh-memory-bridge/lib/merge.js +205 -0
- package/plugins/dsh-memory-bridge/lib/portable.js +314 -0
- package/plugins/dsh-memory-bridge/lib/state.js +565 -0
- package/plugins/dsh-memory-bridge/lib/sync.js +984 -0
- package/plugins/dsh-memory-bridge/locale/en.json +6 -0
- package/plugins/dsh-memory-bridge/locale/ru.json +6 -0
- package/plugins/dsh-memory-bridge/package.json +67 -0
- package/plugins/dsh-peak-guard/README.md +16 -3
- package/plugins/dsh-peak-guard/client.js +223 -8
- package/plugins/dsh-peak-guard/package.json +1 -1
- package/plugins/dsh-plan-mode/README.md +24 -0
- package/plugins/dsh-plan-mode/cordis.patch.yml +3 -0
- package/plugins/dsh-plan-mode/index.js +103 -0
- package/plugins/dsh-plan-mode/lib/plan.js +89 -0
- package/plugins/dsh-plan-mode/package.json +18 -0
- package/plugins/dsh-profile-transfer/client.js +277 -17
- package/plugins/dsh-profile-transfer/index.js +10 -0
- package/plugins/dsh-profile-transfer/package.json +3 -1
- package/plugins/dsh-profile-transfer/transfer.js +101 -2
- package/plugins/dsh-project-memory/index.js +92 -2
- package/plugins/dsh-project-memory/package.json +1 -1
- package/plugins/dsh-repo-map/README.md +26 -0
- package/plugins/dsh-repo-map/cordis.patch.yml +3 -0
- package/plugins/dsh-repo-map/index.js +99 -0
- package/plugins/dsh-repo-map/lib/repo-map.js +170 -0
- package/plugins/dsh-repo-map/package.json +25 -0
- package/plugins/dsh-skill-autopilot/index.js +135 -11
- package/skills/delivery-gate/SKILL.md +31 -0
- package/skills/git-and-ci/SKILL.md +5 -1
- package/skills/manifest.json +31 -6
- package/skills/requirements-and-planning/SKILL.md +3 -0
- package/skills/research-first/SKILL.md +35 -0
- package/skills/research-first/references/query-patterns.md +20 -0
- package/skills/subagent-playbook/SKILL.md +36 -0
- package/skills/subagent-playbook/references/prompts.md +39 -0
- package/skills/ui-craft/SKILL.md +43 -0
- package/skills/visual-qa/SKILL.md +29 -0
- package/skills/visual-qa/references/commands.md +32 -0
- package/skills-baseline.json +219 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server manager: connection lifecycle, tool mapping and status.
|
|
3
|
+
*/
|
|
4
|
+
import { McpClient } from "./client.js";
|
|
5
|
+
|
|
6
|
+
function sanitize(value) {
|
|
7
|
+
return String(value ?? "").replace(/[^A-Za-z0-9_-]+/gu, "_").replace(/^_+|_+$/gu, "").slice(0, 48);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function textFromContent(content) {
|
|
11
|
+
if (!Array.isArray(content)) return "";
|
|
12
|
+
return content
|
|
13
|
+
.map((block) => {
|
|
14
|
+
if (block?.type === "text" && typeof block.text === "string") return block.text;
|
|
15
|
+
if (block?.type === "image") return `[image ${block.mimeType || "image"}]`;
|
|
16
|
+
if (block?.type === "resource") return `[resource ${block.resource?.uri || ""}]`;
|
|
17
|
+
return "";
|
|
18
|
+
})
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join("\n");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class McpManager {
|
|
24
|
+
constructor(ctx) {
|
|
25
|
+
this.ctx = ctx;
|
|
26
|
+
this.clients = new Map();
|
|
27
|
+
this.tools = new Map();
|
|
28
|
+
this.disposers = [];
|
|
29
|
+
this.closed = false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
reopen() {
|
|
33
|
+
this.closed = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
toolName(serverId, toolName) {
|
|
37
|
+
const base = `mcp__${sanitize(serverId)}__${sanitize(toolName)}`;
|
|
38
|
+
if (base.length <= 64 && !this.tools.has(base)) return base;
|
|
39
|
+
let suffix = 2;
|
|
40
|
+
while (`${base.slice(0, 60)}_${suffix}`.length <= 64 && this.tools.has(`${base.slice(0, 60)}_${suffix}`)) suffix += 1;
|
|
41
|
+
return `${base.slice(0, 60)}_${suffix}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async connect(server) {
|
|
45
|
+
if (this.closed) throw new Error("MCP manager is closed");
|
|
46
|
+
const existing = this.clients.get(server.id);
|
|
47
|
+
if (existing?.client?.connected) return existing;
|
|
48
|
+
const client = new McpClient(server, this.ctx.logger);
|
|
49
|
+
try {
|
|
50
|
+
await client.connect();
|
|
51
|
+
if (this.closed) {
|
|
52
|
+
await client.close().catch(() => {});
|
|
53
|
+
throw new Error("MCP manager is closed");
|
|
54
|
+
}
|
|
55
|
+
const tools = await client.listTools();
|
|
56
|
+
const record = { server, client, tools, resources: [], error: "" };
|
|
57
|
+
this.clients.set(server.id, record);
|
|
58
|
+
for (const tool of tools) {
|
|
59
|
+
const name = this.toolName(server.id, tool.name);
|
|
60
|
+
this.tools.set(name, { serverId: server.id, tool, record });
|
|
61
|
+
}
|
|
62
|
+
return record;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
await client.close().catch(() => {});
|
|
65
|
+
const record = { server, client: null, tools: [], resources: [], error: error instanceof Error ? error.message : String(error) };
|
|
66
|
+
this.clients.set(server.id, record);
|
|
67
|
+
return record;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async connectAll(servers) {
|
|
72
|
+
const results = await Promise.allSettled(servers.map((server) => this.connect(server)));
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
registerTools() {
|
|
77
|
+
if (this.closed) return;
|
|
78
|
+
for (const [name, entry] of this.tools) {
|
|
79
|
+
const definition = {
|
|
80
|
+
name,
|
|
81
|
+
description: `MCP ${entry.serverId}/${entry.tool.name}: ${entry.tool.description || "server tool"}`,
|
|
82
|
+
parameters: entry.tool.inputSchema && typeof entry.tool.inputSchema === "object"
|
|
83
|
+
? entry.tool.inputSchema
|
|
84
|
+
: { type: "object", properties: {} },
|
|
85
|
+
async execute(input, exec) {
|
|
86
|
+
const record = entry.record;
|
|
87
|
+
if (!record?.client?.connected) return { ok: false, text: `MCP ${entry.serverId}: server is not connected` };
|
|
88
|
+
try {
|
|
89
|
+
const result = await record.client.callTool(entry.tool.name, input ?? {});
|
|
90
|
+
const text = textFromContent(result?.content) || (result?.structuredContent ? JSON.stringify(result.structuredContent) : "");
|
|
91
|
+
return { ok: result?.isError !== true, text: `MCP ${entry.serverId}/${entry.tool.name}:\n${text}` };
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return { ok: false, text: `MCP ${entry.serverId}/${entry.tool.name}: ${error instanceof Error ? error.message : String(error)}` };
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
const dispose = this.ctx.tools.register(definition);
|
|
99
|
+
if (typeof dispose === "function") this.disposers.push(dispose);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.ctx.logger?.warn?.(`[mcp] tool ${name} skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async call(serverId, toolName, args) {
|
|
107
|
+
const record = this.clients.get(serverId);
|
|
108
|
+
if (!record?.client?.connected) throw new Error(`MCP server not connected: ${serverId}`);
|
|
109
|
+
const result = await record.client.callTool(toolName, args);
|
|
110
|
+
return textFromContent(result?.content) || (result?.structuredContent ? JSON.stringify(result.structuredContent) : "");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async resources(serverId) {
|
|
114
|
+
const record = this.clients.get(serverId);
|
|
115
|
+
if (!record?.client?.connected) throw new Error(`MCP server not connected: ${serverId}`);
|
|
116
|
+
return record.client.listResources();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async readResource(serverId, uri) {
|
|
120
|
+
const record = this.clients.get(serverId);
|
|
121
|
+
if (!record?.client?.connected) throw new Error(`MCP server not connected: ${serverId}`);
|
|
122
|
+
return record.client.readResource(uri);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
status() {
|
|
126
|
+
return [...this.clients.values()].map((record) => ({
|
|
127
|
+
id: record.server.id,
|
|
128
|
+
scope: record.server.scope,
|
|
129
|
+
trusted: record.server.trusted,
|
|
130
|
+
connected: Boolean(record.client?.connected),
|
|
131
|
+
tools: record.tools.length,
|
|
132
|
+
error: record.error,
|
|
133
|
+
serverInfo: record.client?.serverInfo ?? null,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async close() {
|
|
138
|
+
this.closed = true;
|
|
139
|
+
for (const dispose of this.disposers.splice(0)) {
|
|
140
|
+
try { dispose(); } catch { /* best effort */ }
|
|
141
|
+
}
|
|
142
|
+
await Promise.allSettled([...this.clients.values()].map((record) => record.client?.close?.()));
|
|
143
|
+
this.clients.clear();
|
|
144
|
+
this.tools.clear();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-mcp-manager",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"default": "./index.js"
|
|
10
|
+
},
|
|
11
|
+
"./client": {
|
|
12
|
+
"default": "./client.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"description": "MCP servers for DSH: config, trust, tools, resources, Settings UI and direct calls.",
|
|
17
|
+
"license": "MIT"
|
|
18
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# dsh-memory-bridge 2.2
|
|
2
|
+
|
|
3
|
+
**Память проектов между АРМ. Ваш приватный GitHub. Без команд — всё мышкой.**
|
|
4
|
+
|
|
5
|
+
Плагин DeepSeek Harness, который синхронизирует досье `dsh-project-memory` между вашими
|
|
6
|
+
Windows-машинами через **ваш собственный приватный GitHub-репозиторий**. Дополнения с разных
|
|
7
|
+
машин объединяются трёхсторонним слиянием, одинаково изменённая строка становится конфликтом
|
|
8
|
+
и **никогда не затирается молча**.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/dsh-memory-bridge)
|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
**MIT** · проверено на DSH `0.2.2-rc.1` (Windows 11) · без LLM-вызовов, без токенов в работе ·
|
|
18
|
+
у каждого пользователя — свой GitHub-аккаунт и свой репозиторий
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Что нового в 2.2
|
|
23
|
+
|
|
24
|
+
- **История и откат.** У каждого проекта — список commit’ов, которые меняли досье, и кнопка
|
|
25
|
+
«Восстановить». Восстановление сначала сохраняет текущие файлы в бэкап, затем создаёт новый
|
|
26
|
+
commit: ничего не пропадает, восстановление видно в истории Git.
|
|
27
|
+
- **Подсветка конфликтов.** В двух версиях «Моё / С GitHub» изменённые строки подсвечиваются:
|
|
28
|
+
розовые — только у меня, зелёные — только на GitHub. Выбор стал быстрее и безопаснее.
|
|
29
|
+
- **Host-защита приватности.** Перед каждой синхронизацией (не только при подключении)
|
|
30
|
+
проверяется, что репозиторий всё ещё приватный; публичный или неподтверждённый останавливает
|
|
31
|
+
синхронизацию до любых git-операций.
|
|
32
|
+
- **CI**: GitHub Actions прогоняет тесты на каждый push.
|
|
33
|
+
- **В 2.1:** свой GitHub-аккаунт у каждого пользователя, мастер настройки в один клик,
|
|
34
|
+
трёхстороннее слияние, отказ публичному репозиторию.
|
|
35
|
+
|
|
36
|
+
## Как это работает
|
|
37
|
+
|
|
38
|
+
1. Досье проекта лежит в приватном репозитории обмена обычными Markdown-файлами.
|
|
39
|
+
2. При синхронизации плагин получает свежую версию, сливает её с локальной (3-way merge через
|
|
40
|
+
`git merge-file`) и отправляет результат обратно.
|
|
41
|
+
3. Если одна и та же строка изменена по-разному — карточка конфликта с выбором
|
|
42
|
+
«Оставить моё», «Принять с GitHub» или «Объединить».
|
|
43
|
+
|
|
44
|
+
Push не форсируется: если кто-то успел раньше, плагин заново получает изменения и сливает.
|
|
45
|
+
|
|
46
|
+
## Установка
|
|
47
|
+
|
|
48
|
+
В составе **dsh-desktop-kit 2.3+** плагин уже включён — отдельно ставить не нужно.
|
|
49
|
+
|
|
50
|
+
Отдельно: в DSH **Плагины → Add plugin** → `dsh-memory-bridge` → перезапустить окно (`Ctrl+R`).
|
|
51
|
+
|
|
52
|
+
Требования:
|
|
53
|
+
|
|
54
|
+
- [Git for Windows](https://git-scm.com/download/win);
|
|
55
|
+
- [GitHub CLI (`gh`)](https://cli.github.com/) и один раз выполненный вход `gh auth login`
|
|
56
|
+
(используется **ваш** аккаунт — данные не уходят в чужие репозитории).
|
|
57
|
+
|
|
58
|
+
## Первый запуск: 2 клика
|
|
59
|
+
|
|
60
|
+
1. **Настройки → «Память проектов»**. Если `gh` нет или вы не вошли — страница покажет
|
|
61
|
+
точные шаги и кнопку «Проверить снова».
|
|
62
|
+
2. Когда GitHub подключён, страница покажет «GitHub: вы вошли как `<ваш-логин>`» и
|
|
63
|
+
предложит `<ваш-логин>/dsh-memory-exchange`. Нажмите **«Создать приватный репозиторий»**
|
|
64
|
+
(или впишите существующий и нажмите «Подключить»).
|
|
65
|
+
3. Нажмите **«Включить синхронизацию»** — готово. Дальше всё автоматически.
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
### Вторая машина
|
|
70
|
+
|
|
71
|
+
Установите плагин, откройте страницу и подключите **тот же** репозиторий
|
|
72
|
+
(`<ваш-логин>/dsh-memory-exchange`), затем «Включить». Откройте нужную папку проекта в DSH —
|
|
73
|
+
досье подтянется из репозитория, шаблоны заменятся накопленными данными.
|
|
74
|
+
|
|
75
|
+
## Конфликты
|
|
76
|
+
|
|
77
|
+
Если одна и та же строка изменена на двух машинах по-разному, плагин показывает две версии
|
|
78
|
+
рядом и предлагает выбор. Пока конфликт не разрешён, файл не перезаписывается ни локально,
|
|
79
|
+
ни в репозитории.
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
## История и откат
|
|
84
|
+
|
|
85
|
+
В разделе «Проекты» у каждого проекта есть кнопка **«История»**. Она показывает commit’ы,
|
|
86
|
+
которые меняли досье, с датой и автором; кнопка **«Восстановить»** возвращает файлы досье
|
|
87
|
+
к выбранной версии. Текущее состояние сначала уходит в локальный бэкап, а восстановление
|
|
88
|
+
оформляется обычным commit’ом — его видно в истории и можно откатить снова.
|
|
89
|
+
|
|
90
|
+
## Безопасность
|
|
91
|
+
|
|
92
|
+
- **Только приватный репозиторий**: плагин проверяет видимость через `gh` и отказывается
|
|
93
|
+
работать с публичным или неподтверждённым репозиторием.
|
|
94
|
+
- Репозиторий принадлежит вашему GitHub-аккаунту; в поставляемом коде нет чужих владельцев
|
|
95
|
+
и репозиториев.
|
|
96
|
+
- Секреты и PII вырезаются redaction-ом, домашние пути заменяются токеном `%DSH_HOME%`,
|
|
97
|
+
служебные файлы и `node_modules` в репозиторий не попадают.
|
|
98
|
+
- Конфликты не разрешаются «силой»: перед записью делается локальный бэкап, вся история —
|
|
99
|
+
в Git. Плагин не вызывает LLM, не добавляет инструментов и не меняет промпт — **0 токенов**.
|
|
100
|
+
|
|
101
|
+
## Скриншоты
|
|
102
|
+
|
|
103
|
+
### Светлая тема
|
|
104
|
+
|
|
105
|
+

|
|
106
|
+
|
|
107
|
+
### Тёмная тема
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
### Мобильный экран
|
|
112
|
+
|
|
113
|
+
<img src="https://cdn.jsdelivr.net/npm/dsh-memory-bridge@2.2.1/docs/images/settings-mobile.png" alt="Настройки на 390 px" width="390">
|
|
114
|
+
|
|
115
|
+
## Где что лежит
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
GitHub (private, ваш аккаунт): <login>/dsh-memory-exchange
|
|
119
|
+
projects/<проект>--<key8>/
|
|
120
|
+
project.json # fingerprint, имя, автор последней записи
|
|
121
|
+
PROJECT.md … RUNBOOK.md
|
|
122
|
+
JOURNAL/YYYY-MM.md
|
|
123
|
+
|
|
124
|
+
Локально:
|
|
125
|
+
~/.dsh/projects-memory/… # рабочие досье (ведёт dsh-project-memory)
|
|
126
|
+
~/.dsh/memory-sync/
|
|
127
|
+
config.json # репозиторий, ветка, интервал, автосинхронизация
|
|
128
|
+
machine.json # machineId и имя машины
|
|
129
|
+
github/<repo>/ # локальный клон репозитория обмена
|
|
130
|
+
base/<проект>/ # снимок последней успешной синхронизации
|
|
131
|
+
github-conflicts/ # неразрешённые конфликты (для страницы)
|
|
132
|
+
backups/… # копии досье перед записью
|
|
133
|
+
log.txt # диагностика
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Ограничения
|
|
137
|
+
|
|
138
|
+
- Нужны сеть, git и `gh` (одноразовый вход) на каждой машине.
|
|
139
|
+
- Проект должен быть git-репозиторием с remote; не-git проекты показываются как
|
|
140
|
+
«не синхронизируются» (при появлении remote плагин определяет его сам).
|
|
141
|
+
- Шифрование содержимого репозитория не используется — иначе невозможно слияние Markdown;
|
|
142
|
+
приватность обеспечивается приватным репозиторием и redaction-ом.
|
|
143
|
+
- Разрешение конфликта — выбор версии целиком/объединение; построчный diff-редактор —
|
|
144
|
+
в следующей версии.
|
|
145
|
+
|
|
146
|
+
## Разработка
|
|
147
|
+
|
|
148
|
+
```powershell
|
|
149
|
+
cd <путь к dsh-memory-bridge>
|
|
150
|
+
node --test # 35 тестов: два локальных клона, асинхронные добавления, конфликты, redaction,
|
|
151
|
+
# per-user GitHub, отказ публичному репозиторию, отсутствие личных владельцев,
|
|
152
|
+
# Host-проверка приватности и история/откат версий
|
|
153
|
+
npm pack --dry-run # состав npm-пакета
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Скриншоты в `docs/images` генерируются из настоящего `client.js` через preview-harness:
|
|
157
|
+
`node docs/preview/capture.mjs`.
|
|
158
|
+
|
|
159
|
+
## Лицензия
|
|
160
|
+
|
|
161
|
+
MIT — см. [LICENSE](LICENSE).
|