Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .cursor/rules/agent-workflow.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: Правила работы агента в репозитории OPM
alwaysApply: true
---

# Правила работы агента в этом репо

- Меняй только то, что нужно для задачи; без «заодно»-рефакторинга и лишних markdown-файлов.
- Сохраняй русские имена и стиль соседнего кода.
- После существенных правок ядра/CLI — запускай релевантные тесты (`tasks/test.os` или узкий сценарий).
- Коммиты и push — только по явной просьбе пользователя.
- Не коммить секреты (`GITHUB_OAUTH_TOKEN`, `opm.cfg` с токенами/прокси).
- Пользовательский `README.md` обновляй, если меняется публичное поведение CLI.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Добавьте обновление README.md в этот PR.

Сводка PR описывает изменение публичного поведения push: выбора зеркала и порта. В списке изменённых файлов из контекста README.md отсутствует. Добавьте описание этого поведения, чтобы пользовательская документация соответствовала CLI.

Основание: правило в этой строке требует обновлять README.md при изменении публичного поведения CLI, а PR меняет поведение push.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/rules/agent-workflow.mdc at line 13, Обновите пользовательский
README.md, добавив описание изменённого публичного поведения команды push:
выбора зеркала и порта. Убедитесь, что документация отражает фактическое
поведение CLI.

233 changes: 233 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# AGENTS.md — руководство для AI-агентов (OPM)

Документ для агентов, работающих с этим репозиторием. Цель — быстро понять архитектуру, где править код и как проверять изменения.

## Что это за проект

**OPM (OneScript Package Manager)** — менеджер пакетов для [OneScript](https://oscript.io): сборка `.ospx`, установка из хаба/файла/URL, разрешение зависимостей, публикация, scaffold и запуск задач.

- Репозиторий: https://github.com/oscript-library/opm
- Лицензия: Apache-2.0
- Версия продукта: `КонстантыOpm.ВерсияПродукта` (сейчас `1.1.2`)
- Требуемая среда: OneScript ≥ **1.8.3** (`packagedef`)
- Хабы: `http://hub.oscript.io`, запасной `http://hub.oscript.ru`
- Packaging docs: https://hub.oscript.io/packaging

## Стек

| Слой | Технология |
|------|------------|
| Язык | OneScript / BSL (`.os`), **русские** идентификаторы |
| CLI | пакет `cli` |
| Логи | `logos` (`oscript.app.opm`) |
| Unit | `1testrunner` |
| BDD | `1bdd` (Gherkin на русском) |
| Coverage | `coverage` + `oscript -codestat=` |
| CI | GitHub Actions + SonarQube (`sonar.openbsl.ru`) |

Runtime-зависимости — в корневом `packagedef`: `fs`, `asserts`, `fluent`, `logos`, `cli`, `tempfiles`, `gitrunner`, `reflector`.

## Структура репозитория

```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Укажите язык для fenced-блоков.

markdownlint-cli2 сообщает MD040 для строк 32 и 57. Добавьте text к открывающим fence-блокам. Содержимое ASCII-схем не изменится.

Основание: предупреждение MD040 указано статическим анализом.

Also applies to: 57-57

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 32-32: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` at line 32, Update the fenced blocks in AGENTS.md at the locations
corresponding to lines 32 and 57 to specify the text language on their opening
fences, without changing the ASCII diagram contents.

Source: Linters/SAST tools

opm/
├── packagedef # манифест пакета OPM
├── src/
│ ├── cmd/
│ │ ├── opm.os # точка входа CLI
│ │ ├── Классы/ # КомандаOpm_*.os, ИсполнительЗадач
│ │ └── Модули/ # ПараметрыПриложенияOpm
│ └── core/
│ ├── Классы/ # менеджеры, сборщик, установка...
│ └── Модули/ # КонстантыOpm, РаботаС*, НастройкиOpm
├── tasks/ # opm run / opm test
│ ├── test.os # unit + bdd
│ ├── coverage.os # как в CI
│ └── oscript.cfg
├── tests/ # unit-тесты (1testrunner)
├── features/ # BDD (1bdd) + step_definitions/
├── .github/workflows/ # CI / release / rebase
└── oscript_modules/ # локальные зависимости (vendor, в .gitignore)
```

Отдельного каталога `docs/` нет — ориентир: `README.md` и этот файл.

## Архитектура (слои)

```
src/cmd/opm.os (cli.КонсольноеПриложение)
→ КомандаOpm_* (ОписаниеКоманды / ВыполнитьКоманду)
→ РаботаСПакетами / СборщикПакета / ИсполнительЗадач / ...
→ МенеджерУстановкиПакетов / МенеджерПолученияПакетов / УстановкаПакета / ...
```

### Карта «хочу изменить X»

| Задача | Куда смотреть |
|--------|----------------|
| CLI-команда / флаги | `src/cmd/opm.os`, `src/cmd/Классы/КомандаOpm_*.os` |
| Install / зависимости | `РаботаСПакетами`, `МенеджерУстановкиПакетов`, `УстановкаПакета`, `КэшУстановленныхПакетов` |
| Скачивание с хаба | `МенеджерПолученияПакетов`, `СерверПакетов`, `КонстантыOpm` |
| Сборка `.ospx` | `СборщикПакета`, `ОписаниеПакета`, `СериализацияМетаданныхПакета` |
| Publish | `КомандаOpm_Push` |
| Версии `Имя@Версия` | `РаботаСВерсиями` |
| Чтение `packagedef` | `РаботаСОписаниемПакета`, `ОписаниеПакета` |
| `opm.cfg` / прокси / зеркала | `ПараметрыПриложенияOpm`, `НастройкиOpm` |
| Версия продукта | `src/core/Модули/КонстантыOpm.os` (+ fallback в `packagedef`) |

### Команды CLI

| Команда | Класс |
|---------|-------|
| `a app` | `КомандаOpm_App` |
| `b build` | `КомандаOpm_Build` |
| `c config` | `КомандаOpm_Config` |
| `i install` | `КомандаOpm_Install` |
| `ls list` | `КомандаOpm_List` |
| `pre prepare` | `КомандаOpm_Prepare` |
| `p push` | `КомандаOpm_Push` |
| `r run` | `КомандаOpm_Run` |
| `test` | `КомандаOpm_Test` |
| `u update` | `КомандаOpm_Update` |
| `version` | `КомандаOpm_Version` |

### Потоки данных (кратко)

**Install:** CLI → `РаботаСПакетами` → download (`МенеджерПолученияПакетов`) → `УстановкаПакета` (unzip `.ospx`) → рекурсивные зависимости → кэш установленных.

**Build:** `СборщикПакета` читает `packagedef` (контекст `Описание` = fluent `ОписаниеПакета`) → hooks → `{Имя}-{Версия}.ospx` = ZIP(`opm-metadata.xml` + `content.zip`).

**Режимы установки:** локально → `./oscript_modules`; глобально → системный `lib` OneScript (`РежимУстановкиПакетов`).

## Окружение и команды

### Подготовка

```powershell
# Нужен OneScript ≥ 1.8.3 (stable или 1.8.4 как в CI)
opm install opm
opm install 1testrunner
opm install 1bdd
opm install coverage
opm install -l --dev
```

### Запуск из исходников

```powershell
oscript src\cmd\opm.os --help
oscript src\cmd\opm.os version
oscript src\cmd\opm.os install --local
oscript src\cmd\opm.os build --mf .\packagedef .
```

Отладка: `.vscode/launch.json`, `LOGOS_CONFIG=logger.oscript.app.opm=DEBUG`.

### Тесты

```powershell
oscript tasks\test.os # unit + bdd
oscript tasks\coverage.os # как в CI
opm test # через CLI
```

Отчёты: каталог `out/`.

### Типовые CLI-вызовы

```powershell
opm install asserts
opm install --local # зависимости packagedef → ./oscript_modules
opm install --local --dev
opm install -f my.ospx --local
opm install Package@1.2.0
opm build --mf .\packagedef .
opm list
opm list --remote
opm prepare my-package
opm update opm
```

Полезные переменные: `OSCRIPTBIN`, `OPM_HUB_MIRROR`, `OPM_HUB_CHANNEL`, `GITHUB_OAUTH_TOKEN`, `LOGOS_CONFIG`.

## Соглашения по коду

1. **Русский BSL** — имена процедур, переменных, каталогов `Классы/` / `Модули/`.
2. Пользовательские строки — через `НСтр("ru='...';en='...')`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Исправьте синтаксис примера НСтр.

В строке 156 отсутствует закрывающая кавычка перед ). Пример нельзя корректно скопировать в BSL.

Исправление
-2. Пользовательские строки — через `НСтр("ru='...';en='...')`.
+2. Пользовательские строки — через `НСтр("ru='...';en='...'")`.

As per coding guidelines: пользовательские строки должны оформляться через НСтр("ru='...';en='...'").

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
2. Пользовательские строки — через `НСтр("ru='...';en='...')`.
2. Пользовательские строки — через `НСтр("ru='...';en='...'")`.
🧰 Tools
🪛 LanguageTool

[typographical] ~156-~156: Непарный символ: «"» скорей всего пропущен
Context: ...и/. 2. Пользовательские строки — через НСтр("ru='...';en='...')`. 3. Модули = статиче...

(RU_UNPAIRED_BRACKETS)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` at line 156, Исправьте пример в разделе о пользовательских
строках: в синтаксисе НСтр добавьте закрывающую кавычку перед закрывающей
скобкой, чтобы пример соответствовал формату НСтр("ru='...';en='...'") и
корректно копировался в BSL.

Sources: Coding guidelines, Linters/SAST tools

3. Модули = статический API (`РаботаС*`, `КонстантыOpm`); классы = состояние (`Менеджер*`, `УстановкаПакета`).
4. Подключения: `#Использовать logos`, `#Использовать "../core"`, `#Использовать cli`.
5. Публичный API пакета задаётся в `packagedef` через `.ОпределяетКласс` / `.ОпределяетМодуль`.
6. OneScript подхватывает классы/модули по имени файла из `Классы/` и `Модули/`.
7. Комментарии — на русском. Не рефакторить стиль «заодно», если задача этого не требует.

### Добавление CLI-команды

1. Создать `src/cmd/Классы/КомандаOpm_Foo.os` по образцу `КомандаOpm_Build.os`:
- `ОписаниеКоманды(КомандаПриложения)` — опции и аргументы;
- `ВыполнитьКоманду(КомандаПриложения)` — логика.
2. Зарегистрировать в `src/cmd/opm.os`:
`Приложение.ДобавитьКоманду("f foo", НСтр(...), Новый КомандаOpm_Foo);`
3. При необходимости — unit/BDD и строка в `README.md`.

**Не копировать** `ШаблонКоманды.os-template` — там устаревший cmdline API. Актуальный паттерн — пакет `cli`, как в `КомандаOpm_Build.os`.

### Bump версии OPM

1. `ВерсияПродукта` в `src/core/Модули/КонстантыOpm.os`
2. Fallback-строка в `packagedef` (`Иначе ВерсияПродукта = "..."`)
3. Сборка / тесты / release workflow

### Зависимости самого OPM

В корневом `packagedef`:

```bsl
.ЗависитОт("имя", "min.version")
.РазработкаЗависитОт("имя", "min.version")
```

Затем `opm install -l` / `opm install -l --dev`.

## Тесты: контракт

**Unit** (`tests/*.os`, 1testrunner):

- `ПолучитьСписокТестов(Тестирование)`
- `ПередЗапускомТеста` / `ПослеЗапускаТеста`
- методы `ТестДолжен_*`
- asserts: `#Использовать asserts`, `Ожидаем`

Файлы: `packagedef-test.os`, `versions-test.os`, `mft-serializer-test.os`, `pkg-cache.os`, `packagelist.os`, `download.os`, `build-install-test.os`.

**BDD** (`features/*.feature` + `features/step_definitions/*.os`, `# language: ru`):

- `opm-build.feature`, `install-file.feature`, `Настройки.feature`

При правках логики — добавляй/обновляй ближайший тест в той же области (см. карту выше).

## CI

`.github/workflows/main.yml`:

- матрица: ubuntu / windows / macos × oscript `stable` и `1.8.4`
- установка deps → `oscript ./tasks/coverage.os`
- Sonar только на `ubuntu-latest` + `stable`

Release: `.github/workflows/release.yml` (маска `opm-*.ospx`).

Перед PR желательно прогнать `oscript tasks\test.os` (или `coverage.os`).

## Критичные ограничения и ловушки

1. **CLI ≥ 0.15:** опции **до** аргументов. Правильно: `opm build --mf ./packagedef .`. Неправильно: `opm build . -mf ...`.
2. **Версия продукта** дублируется: `КонстантыOpm` и fallback в `packagedef` — менять согласованно.
3. Формат `.ospx`: внешний ZIP → `opm-metadata.xml` + `content.zip`; не путать уровни.
4. Имена пакетов на хабе **регистрозависимы** при скачивании, сравнение имён — нет.
5. Разрешение зависимостей слабое: для уже установленного пакета фактически проверяется min-версия; max используется ограниченно.
6. Локальный `-dest` игнорируется при `--local`.
7. Канал `push` `auto` только из git-ветки **`master`** (не `main`).
8. Hooks манифеста (`ПередСборкой` / `ПриСборке` / `ПослеСборки`, `ПередУстановкой` / `ПриУстановке`) вызываются через рефлектор — не ломать сигнатуры.
9. На Linux пользовательский конфиг — **`.opm.cfg`** (с точкой); на Windows — `opm.cfg` в `%USERPROFILE%`. Приоритет: cwd → user → system → каталог opm.
10. Хабы по умолчанию — **http**, не https.
Comment on lines +230 to +231

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Зафиксируйте контракт необязательного порта зеркала.

В руководстве перечислены конфигурация и зеркала, но не описано новое поведение порта. Укажите, что отсутствующий порт сохраняется как Неопределено, а соединение выбирает значение из настройки или корректное значение по умолчанию. Иначе агент может снова считать порт обязательным или подставить значение слишком рано.

Основание: сводка PR и контекст слоя фиксируют необязательный порт и выбор порта при подключении.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` around lines 230 - 231, Обновите раздел о конфигурации и зеркалах
в AGENTS.md, зафиксировав контракт необязательного порта зеркала: при отсутствии
порт сохраняется как Неопределено, а при установлении соединения выбирается порт
из настройки либо корректное значение по умолчанию. Не делайте порт обязательным
и не подставляйте значение раньше этапа подключения.

11. `oscript_modules` в `.gitignore`, но в `packagedef` есть `.ВключитьФайл("oscript_modules")` (бандл в дистрибутив).
12. Кириллические пути (`Классы`, `Модули`): на Windows учитывать кодировку консоли при запуске задач.
20 changes: 17 additions & 3 deletions src/cmd/Классы/КомандаOpm_Push.os
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
.Перечисление("stable", "stable", "Канал содержащий стабильные версии пакетов")
.Перечисление("dev", "dev" , "Канал содержащий разработческие версии пакетов")
.ВОкружении("OPM_HUB_CHANNEL");
ОпцияЗеркала = КомандаПриложения.Опция("m mirror", "СерверУдаленногоХранилища", "Имя сервера для публикации.
ОпцияЗеркала = КомандаПриложения.Опция("m mirror", "ОсновнойСерверПакетов", "Имя сервера для публикации.
| Доступные сервера прописываются в конфигурационном файле opm.cfg, параметр 'СервераПакетов'.")
.ВОкружении("OPM_HUB_MIRROR")
.ТПеречисление();
Expand Down Expand Up @@ -135,22 +135,29 @@

КонецФункции

Функция ПортПоУмолчанию(Знач Сервер)

Возврат ?(СтрНачинаетсяС(НРег(Сервер), "https://"), 443, 80);

КонецФункции

Процедура ОтправитьПакетВХаб(Знач ТокенАвторизации, Знач ФайлПакета, Знач Канал, Знач ИмяСервераПакетов)

ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ФайлПакета.ПолноеИмя);
ДвоичныеДанныеФайлаВBase64 = Base64Строка(ДвоичныеДанныеФайла);

ДоступныеСервераПакетов = НастройкиOpm.ПолучитьНастройки().СервераПакетов;

// Для настроек по умолчанию
Сервер = КонстантыOpm.СерверУдаленногоХранилища;
Ресурс = КонстантыOpm.РесурсПубликацииПакетов;
Порт = Неопределено;

Для Каждого НастройкаСервера Из ДоступныеСервераПакетов Цикл

Если СтрСравнить(НастройкаСервера.Имя, ИмяСервераПакетов) = 0 Тогда
Сервер = НастройкаСервера.Сервер;
Ресурс = НастройкаСервера.РесурсПубликацииПакетов;
Порт = НастройкаСервера.Порт;
Прервать;
КонецЕсли;

Expand All @@ -164,7 +171,14 @@
Заголовки.Вставить("FILE-NAME", ФайлПакета.Имя);
Заголовки.Вставить("CHANNEL", Канал);

Соединение = Новый HTTPСоединение(Сервер);
Порт = ?(Порт = Неопределено, ПортПоУмолчанию(Сервер), Порт);
Настройки = НастройкиOpm.ПолучитьНастройки();
Если Настройки.ИспользоватьПрокси Тогда
НастройкиПрокси = НастройкиOpm.ПолучитьИнтернетПрокси();
Соединение = Новый HTTPСоединение(Сервер, Порт, , , НастройкиПрокси);
Иначе
Соединение = Новый HTTPСоединение(Сервер, Порт);
КонецЕсли;
Comment on lines +174 to +181

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Добавьте тесты для контракта необязательного порта.

Изменённая логика должна иметь unit- или BDD-тест. Проверьте порт из конфигурации, отсутствие порта для HTTP и HTTPS, а также применение прокси при публикации.

  • src/cmd/Классы/КомандаOpm_Push.os#L174-L181: проверьте создание соединения с явным портом, портом 80 и портом 443.
  • src/cmd/Модули/ПараметрыПриложенияOpm.os#L62-L67: проверьте сохранение Неопределено, когда поле Порт отсутствует.
  • src/core/Модули/НастройкиOpm.os#L106-L106: проверьте значение по умолчанию параметра Порт.
  • src/core/Модули/НастройкиOpm.os#L124-L125: проверьте встроенные серверы без явно заданного порта.

As per coding guidelines, «При изменении логики добавлять или обновлять ближайший unit- или BDD-тест в соответствующей области».

📍 Affects 3 files
  • src/cmd/Классы/КомандаOpm_Push.os#L174-L181 (this comment)
  • src/cmd/Модули/ПараметрыПриложенияOpm.os#L62-L67
  • src/core/Модули/НастройкиOpm.os#L106-L106
  • src/core/Модули/НастройкиOpm.os#L124-L125
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cmd/Классы/КомандаOpm_Push.os` around lines 174 - 181, Добавьте или
обновите ближайшие unit- или BDD-тесты для контракта необязательного порта: в
src/cmd/Классы/КомандаOpm_Push.os:174-181 проверьте явный порт, значения 80 и
443, а также применение прокси; в src/cmd/Модули/ПараметрыПриложенияOpm.os:62-67
проверьте сохранение Неопределено при отсутствии поля Порт; в
src/core/Модули/НастройкиOpm.os:106-106 проверьте значение порта по умолчанию; в
src/core/Модули/НастройкиOpm.os:124-125 проверьте встроенные серверы без явно
заданного порта.

Sources: Coding guidelines, Learnings

Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);
Запрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@
Для каждого ТекущийСерверПакетов Из СервераПакетов Цикл

Сервер = ПолучитьЗначение(ТекущийСерверПакетов, "Сервер", "");
Порт = Число(ПолучитьЗначение(ТекущийСерверПакетов, "Порт", 80));
// Порт 80 не является корректным дефолтом и зависит от протокола.
// поэтому если в конфиге ничего не указано, то определение порта уходит на прикладной уровень и дефолты HTTPСоединение
Порт = ПолучитьЗначение(ТекущийСерверПакетов, "Порт", Неопределено);
Если Не Порт = Неопределено Тогда
Порт = Число(Порт);
КонецЕсли;
ПутьНаСервере = ПолучитьЗначение(ТекущийСерверПакетов, "ПутьНаСервере", "/");
Имя = ПолучитьЗначение(ТекущийСерверПакетов, "Имя", СтрШаблон("ДопСервер_%1", Индекс));
РесурсПубликацииПакетов = ПолучитьЗначение(ТекущийСерверПакетов, "РесурсПубликацииПакетов", "/");
Expand Down
Loading
Loading