dsh-edt-projects 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,21 +11,29 @@ SonarQube/GitLab.
11
11
  остальных проектах в контексте остаётся только `edt_config`, которым проект и
12
12
  включается.
13
13
 
14
+ В **linked git worktree** плагин поднимает отдельное окружение EDT
15
+ (`<worktree>/build/ws`, свой порт и логи), импортирует проекты и монтирует
16
+ `mcp__edt__*` туда; включённость и настройки наследуются от основного checkout.
17
+ Управление — инструмент `edt_workspace`.
18
+
14
19
  ## Установка
15
20
 
16
21
  ```text
17
22
  plugin_manager { action: "install_bundle", target: "dsh-edt-projects" }
18
23
  ```
19
24
 
20
- Нужны EDT 2026.1/2026.2 с установленным EDT-MCP и запущенным MCP-сервером
21
- (по умолчанию `127.0.0.1:8765`) — подробности и настройка в
25
+ Нужны EDT 2026.1/2026.2 с установленным EDT-MCP. В основном checkout MCP-сервер
26
+ запускается вручную (по умолчанию `127.0.0.1:8765`); в worktree плагин находит
27
+ установку EDT сам или по `workspace.home`/`DSH_1C_EDT_HOME` и поднимает её.
28
+ Подробности и настройка — в
22
29
  [README репозитория](https://github.com/pravets/dsh-edt-projects#readme).
23
30
 
24
31
  ## Что внутри
25
32
 
26
33
  - `lib/` — хаб MCP-серверов: активация по проекту, монтаж по требованию
27
34
  (`edt_pin`), настройки на проект, правила для системного промпта, провайдер
28
- навыков.
35
+ навыков; `repo.mjs`/`workspace.mjs`/`workspace-run.mjs` — определение worktree,
36
+ подъём и остановка EDT-окружения в `build/`.
29
37
  - `skills/` — навыки `edt-mcp-project-sonar` и `edt-mcp-project-gitlab`,
30
38
  включаются флагами `sonar.enabled` и `gitlab.enabled` внутри включённого
31
39
  проекта.
package/cordis.patch.yml CHANGED
@@ -27,9 +27,19 @@
27
27
  # Флаги tests включают правила тестируемости: YAxUnit — закладывать тестируемость
28
28
  # и писать тесты в указанный проект, Vanessa — предлагать сценарные тесты.
29
29
  #
30
+ # Git worktree: плагин сам поднимает отдельный EDT workspace в
31
+ # <worktree>/build/ws со своим портом MCP-сервера и импортом проектов, а
32
+ # включённость и настройки наследует от основного checkout. Секция настроек:
33
+ # "workspace": { "home": { "win32": "C:\\Program Files\\1C\\1CE\\components\\1c-edt-..." },
34
+ # "import": "auto", "ports": { "min": 20000, "max": 39999 },
35
+ # "stopOnSessionEnd": true }
36
+ # Управление из сессии — инструмент edt_workspace (status/init/stop/restart/import).
37
+ #
30
38
  # Переменные окружения (префикс DSH_1C_ — чтобы ни с чем не пересекаться):
31
39
  # EDT_MCP_URL адрес MCP-сервера EDT (по умолчанию 127.0.0.1:8765)
32
40
  # EDT_MCP_TOKEN токен, если он включён в EDT
41
+ # DSH_1C_EDT_HOME каталог установки EDT для worktree (перекрывает workspace.home)
42
+ # DSH_1C_EDT_IMPORT 1/0 — импортировать проекты в workspace worktree
33
43
  # DSH_1C_WIKI пути к вики через «;»
34
44
  # DSH_1C_WIKI_ENTRY входная точка вики, например wiki/index.md
35
45
  # DSH_1C_BSL_LS_JAR готовый bsl-language-server jar (главнее всего)
package/lib/bsl.mjs CHANGED
@@ -70,7 +70,9 @@ export function bslSettings(env = process.env) {
70
70
  cacheDir: first(env, 'DSH_1C_BSL_LS_CACHE') ?? join(env.DSH_HOME ?? join(homedir(), '.dsh'), 'cache', 'bsl-language-server'),
71
71
  java: first(env, 'DSH_1C_BSL_LS_JAVA', 'BSL_LS_JAVA') ?? 'java',
72
72
  cwd: first(env, 'DSH_1C_BSL_LS_CWD', 'BSL_LS_CWD') ?? '',
73
- root: first(env, 'DSH_1C_BSL_LS_ROOT', 'DSH_1C_BSL_LS_CWD', 'BSL_LS_CWD') ?? process.cwd(),
73
+ // Пусто = «взять корень проекта»; подставляется в bslSettingsFor, где
74
+ // известен каталог сессии (для worktree — корень worktree, а не cwd DSH).
75
+ root: first(env, 'DSH_1C_BSL_LS_ROOT', 'DSH_1C_BSL_LS_CWD', 'BSL_LS_CWD') ?? '',
74
76
  port: first(env, 'DSH_1C_BSL_LS_PORT'),
75
77
  mode: (first(env, 'DSH_1C_BSL_LS_MODE') ?? 'hybrid').toLowerCase(),
76
78
  downloadAllowed: download === undefined ? true : !isOff(download),
package/lib/config.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  // без перезапуска DSH; разбор кэшируется по mtimeMs.
6
6
  import { existsSync, readFileSync, statSync } from 'node:fs';
7
7
  import { homedir } from 'node:os';
8
- import { join } from 'node:path';
8
+ import { join, resolve } from 'node:path';
9
9
 
10
10
  const USER_FILE = 'dsh-1c.json';
11
11
  const PROJECT_FILE = '.dsh-1c.json';
@@ -73,10 +73,37 @@ export function mergeLayers(...layers) {
73
73
  }
74
74
 
75
75
  /**
76
- * Собирает действующие настройки: база (патч) <- пользователь <- проект.
76
+ * Каталоги, чьи `.dsh-1c.json` образуют слои проекта.
77
+ *
78
+ * Обычная сессия передаёт только `cwd`. Для worktree вызывающий передаёт
79
+ * `[cwd, repo, mainRoot]`: настройки основного checkout (в нём файл может быть
80
+ * незакоммиченным) действуют и в worktree, а файл самого worktree — сильнее.
81
+ * Порядок сохраняется, дубликаты путей схлопываются.
82
+ */
83
+ export function projectDirs({ cwd, projectCwds } = {}) {
84
+ const list = Array.isArray(projectCwds) && projectCwds.length > 0 ? projectCwds : [cwd];
85
+ const seen = new Set();
86
+ const out = [];
87
+ for (const dir of list) {
88
+ if (typeof dir !== 'string' || dir === '') continue;
89
+ let key;
90
+ try {
91
+ key = resolve(dir);
92
+ } catch {
93
+ continue;
94
+ }
95
+ if (seen.has(key)) continue;
96
+ seen.add(key);
97
+ out.push(dir);
98
+ }
99
+ return out;
100
+ }
101
+
102
+ /**
103
+ * Собирает действующие настройки: база (патч) <- пользователь <- файлы проектов.
77
104
  * @returns {{ config: object, sources: string[], errors: string[] }}
78
105
  */
79
- export function resolveConfig({ cwd, env = process.env, base = {} } = {}) {
106
+ export function resolveConfig({ cwd, projectCwds, env = process.env, base = {} } = {}) {
80
107
  const sources = [];
81
108
  const errors = [];
82
109
 
@@ -84,12 +111,18 @@ export function resolveConfig({ cwd, env = process.env, base = {} } = {}) {
84
111
  if (user.error !== undefined) errors.push(user.error);
85
112
  if (user.config !== undefined) sources.push(userConfigPath(env));
86
113
 
87
- const projectPath = projectConfigPath(cwd);
88
- const project = readJsonConfig(projectPath);
89
- if (project.error !== undefined) errors.push(project.error);
90
- if (project.config !== undefined && projectPath !== undefined) sources.push(projectPath);
114
+ const layers = [];
115
+ for (const dir of projectDirs({ cwd, projectCwds })) {
116
+ const projectPath = projectConfigPath(dir);
117
+ if (projectPath === undefined) continue;
118
+ const project = readJsonConfig(projectPath);
119
+ if (project.error !== undefined) errors.push(project.error);
120
+ if (project.config === undefined) continue;
121
+ sources.push(projectPath);
122
+ layers.push(project.config);
123
+ }
91
124
 
92
- return { config: mergeLayers(base, user.config, project.config), sources, errors };
125
+ return { config: mergeLayers(base, user.config, ...layers), sources, errors };
93
126
  }
94
127
 
95
128
  /**
package/lib/enable.mjs CHANGED
@@ -89,3 +89,31 @@ export function activationFor({ cwd, config, hasProjectFile = false } = {}, opti
89
89
  export function isEnabledFor(input, options = {}) {
90
90
  return activationFor(input, options).enabled;
91
91
  }
92
+
93
+ /**
94
+ * Активация с учётом путей worktree.
95
+ *
96
+ * Обычная проверка идёт от каталога сессии. Для worktree дополнительно
97
+ * проверяются его корень и основной checkout: запись `projects` может называть
98
+ * основной checkout, а файл `.dsh-1c.json` может лежать только в нём (не
99
+ * закоммичен). Сильнее всего остаётся `enabled` из объединённых настроек.
100
+ * @param input.cwd каталог сессии (может быть корнем task space).
101
+ * @param input.paths дополнительные пути: корень worktree, основной checkout.
102
+ */
103
+ export function activationForPaths({ cwd, config, hasProjectFile = false, paths = [] } = {}, options = {}) {
104
+ const direct = activationFor({ cwd, config, hasProjectFile }, options);
105
+ if (direct.reason !== 'not-selected') return direct;
106
+
107
+ const projects = Array.isArray(config?.projects) ? config.projects : [];
108
+ const seen = new Set([normalizeProjectPath(cwd, options)]);
109
+ for (const candidate of paths) {
110
+ const key = normalizeProjectPath(candidate, options);
111
+ if (key === undefined || seen.has(key)) continue;
112
+ seen.add(key);
113
+ const entry = projects.find((item) => matchesProject(candidate, item, options));
114
+ if (entry !== undefined) {
115
+ return { enabled: true, reason: 'allowlist', entry, path: candidate, inherited: true };
116
+ }
117
+ }
118
+ return direct;
119
+ }