Двадцать один py-порт печатал ошибки в stderr, тогда как их PS-мастера пишут
через Write-Host в stdout. Счётчики совпадали один в один (14↔14, 19↔19,
13↔13) — сообщения были те же, разъехался только поток. Это нарушало
соответствие из docs/python-porting-guide.md, где Write-Host сопоставлен
обычному print.
Это не косметика. Харнесс не чередует потоки, а группирует: сначала весь
stderr, потом весь stdout. Из-за этого в py-порте вердикт «Error dumping
configuration (code: 1)» печатался ПЕРЕД строками, которые его объясняют, а
причина из лога платформы оказывалась в самом низу — причинный порядок вывода
переворачивался. Порт, работающий на macOS, читался хуже того, что работает на
Windows.
Тесты этого не ловили по построению: текст ошибки сверяют только кейсы со
строковым expectError, а он смотрит в stderr — потому такие кейсы есть лишь у
семейства, где потоки сходятся, а в db-* их ноль.
Добавлен гард check-error-streams.mjs: нет записи в stderr в PS-порте — не
должно быть и в py, и симметрично. Он сразу нашёл пять навыков сверх тех, что
я насчитал вручную, и отсеял два ложных срабатывания (в meta-remove слово
Write-Error стоит в комментарии «почему НЕ Write-Error»).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Список без режима давал молчаливо неверный результат, причём разный. У выгрузки
умолчание Changes игнорировало -Objects и отдавало «изменённое с прошлой
выгрузки». У загрузки на движке 1cv8 умолчание Full игнорировало -Files и
заменяло всю конфигурацию базы, тогда как на ibcmd та же команда выполнялась
частично — одни и те же аргументы вели себя по-разному.
Теперь перечисленные объекты или файлы сами задают частичную операцию. Режим
разрешается до ветвления на движки, поэтому расхождение исчезает по построению;
проверено на стенде: обе ветки дают одинаковый результат.
Заданный вместе со списком -Mode Full или Changes не отбрасывается молча — о нём
сообщает [note]. Список с -Mode UpdateInfo отвергается: это другая операция,
обновление ConfigDumpInfo без выгрузки файлов, и вывод подменил бы её.
Инструкции намеренно не менялись: -Mode Partial остаётся каноничной формой,
а вывод режима — прощающим вводом, который мы не документируем.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверка прямым вызовом платформы на darwin: /ConfigurationRepositoryF"путь с
пробелом" отвергается, тот же ключ без кавычек проходит. Значит на POSIX лишние
и обрамляющие кавычки значений, и кавычки внутри склеенных ключей — они нужны
только для склейки команды на Windows.
File="…" не трогаем: там кавычки часть синтаксиса строки соединения, и с ними
на POSIX всё работает (проверено db-create).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Воспроизведено на darwin: db-load-xml с путём, содержащим пробел, падает с
«Неопределена информационная база». На POSIX аргументы уходят списком, и
кавычки, нужные для склейки команды на Windows, становятся частью значения.
Тот же механизм ранее молча терял многословный -comment в db-repo.
Правка в общей run_v8 (семья platform: run_v8) — чинит все двенадцать
потребителей разом. Снимается ОДИН слой обрамляющих кавычек, поэтому склеенные
ключи (/N"user", File="…", /ConfigurationRepositoryF"путь") не задеты: у них
кавычки внутри токена, там их ждёт разборщик 1С.
PS1 не затронут: там команда всегда склеивается в строку. Точечный arg_value(),
добавленный в db-repo при разборе дефекта, откачен — семейная правка делает его
лишним.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Установлен crserver 8.3.23, поднят на порту 1542 — сетевое хранилище перестало
быть непроверенным местом. Адрес tcp://<хост>[:<порт>]/<имя>, порт по умолчанию
1542; хранилище создаётся сервером по требованию. Весь цикл, файл списка
объектов и администрирование работают так же, как на файловом, и одинаково в
обоих портах. Отдельного поведения у сетевого хранилища не обнаружено.
Нашлась двусмысленность: недоступный сервер даёт «Соединение с хранилищем
конфигурации не установлено» — ровно то же сообщение, что и отсутствие
реквизитов. Прежняя подсказка «добавьте repository в реестр» в сетевом случае
уводила не туда. Теперь db-repo различает по схеме адреса и советует проверить
сервер и порт, а подсказка db-load-xml и db-load-git называет обе причины.
Документация больше не оговаривается «не проверено»: форма адреса подтверждена
на стенде.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Пункт плана, остававшийся открытым: каталог исходников расширения модель
указывала руками, хотя он объявлен в записи базы. Каталог выбирает модель, а не
скрипт, поэтому это строка в инструкции — рядом с такой же про configSrc.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Порт .py зеркалит .ps1 по порядку функций, именам и комментариям — расхождения
только там, где их диктует рантайм. Общие функции взяты из соседних портов
дословно.
Копии зарегистрированы в check-inline-drift.mjs: db-repo присоединён к шести
платформенным семьям, четыре функции блока реквизитов хранилища заведены новыми
семьями, разбор сообщений хранилища — семьёй с эталоном db-load-xml. Гард сразу
нашёл настоящее расхождение: копии Get-RepositoryArgs в четырёх соседях остались
со старым comma-return, эталон правился позже. Ресинхронизировано.
Разбор сообщений хранилища добавлен и в db-load-git: он тоже грузит в базу
частично и получает те же три отказа платформы.
Подкоманда стала именованной (-Command): в группе скрипты принимают только
именованные параметры, слэш-форму в них переводит модель. Позиционный параметр
у пишущего навыка запрещён гардом check-positional-binding. Mandatory при этом
не ставим — обязательный параметр PowerShell запрашивает интерактивно, а в
пакетном запуске это зависание.
Все девять гардов и функциональные тесты db-* зелёные в обоих рантаймах.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Значение с пробелом без кавычек разрывалось на токены, и лишние молча растекались по свободным
позиционным параметрам. На документированной форме вызова (powershell -NoProfile -File):
form-compile.ps1 -JsonPath a.json -OutputPath b.xml -Purpose Форма списка
→ Purpose=[Форма] ObjectPath=[списка], ошибки НЕТ
Навык отрабатывал успешно с усечённым значением; реальный триггер — путь с пробелом в -EmitDsl
или -ObjectPath, результат уезжал не туда молча. У навыков с парой -DefinitionFile/-Operation
осколок уезжал в -DefinitionFile, и навык жаловался на параметр, которого в команде не было.
Расхождение портов: py на том же вызове отвечает «unrecognized arguments: списка» — там все
аргументы объявлены опциями. Правка выравнивает PS по py, python не менялся.
41 навык получает [CmdletBinding(PositionalBinding=$false)] и ни одного позиционного параметра.
Без Position=0 (в отличие от read-only навыков): «главный» параметр механически не выводится —
у meta-edit первым объявлен -DefinitionFile, а путь к объекту вторым, — а ошибка выбора тестами
не ловится, раннер передаёт только именованные флаги. Позиционной формы вызова нет ни в одной из
140 строк SKILL.md, внутренние вызовы навыков друг из друга тоже именованные.
check-positional-binding.mjs расширен на пишущие навыки статической проверкой. Поведенческая
(канареечный файл) остаётся только у read-only: у web-stop и db-run все параметры
Mandatory=$false, связывание прошло бы и тело выполнилось — гард остановил бы Apache и запустил 1С.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Платформа рапортует об успехе (exit 0) и одновременно пишет в /Out-лог, что часть
метаданных отброшена. Детектор этого был только в db-load-xml, хотя db-load-git гоняет
тот же /LoadConfigFromFiles (и дописывает /UpdateDBCfg в тот же вызов), а db-update —
вторую половину той же цепочки. Оба лог печатали, но не разбирали.
Детектор извлечён в Find-SilentRejections / find_silent_rejections и внесён в реестр
семей check-inline-drift: инлайн-код гард сверять не умеет, а именно расхождение копий
и было бы главным риском такого дублирования.
Добавлен восьмой паттерн — «Для работы с конфигурацией необходима версия платформы не
меньше». Замерено при работе над issue #63: конфигурация с режимом совместимости выше
платформы грузится с кодом 0, db-update тоже отвечает 0, объекты в базу не попадают, а
отказ приходит только в рантайме. Строка обрезана до инвариантной части — конкретная
версия в сообщении меняется.
Текст предупреждения переписан. Убрана подсказка «pass -StrictLog to treat as error»:
ключ предназначен для регрессов (его передаёт verify-snapshots), а совет бессмысленный —
операция уже выполнена, и повторять её ради того же текста незачем. Формулировка больше
не утверждает «dropped properties/refs»: класс проблемы разный, а строки лога печатаются
следом и говорят за себя.
Две правки по дороге:
- `return ,$found` в паре с `@()` у вызывающего давал массив из одного пустого массива,
то есть предупреждение «1 problem(s)» на чистом логе. Возврат без запятой-обёртки.
- py-порт писал предупреждение в stderr, PS1 — в stdout. В самом py-порте stderr занят
исключительно фатальными «Error:» перед exit 1, так что не-фатальное предупреждение там
было единственным исключением. Приведено к stdout — и к соглашению своего же файла, и к
поведению PS1; verify-snapshots на падении читает `stderr || stdout`, поэтому диагностика
не теряется.
Тесты: фейковая платформа .cmd, которая вычитывает путь из /Out и кладёт туда готовый лог,
— по четыре кейса на db-load-xml и db-update (отбраковка, она же под -StrictLog, новый
паттерн, чистый лог без предупреждения). Раньше детектор не был покрыт вообще.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Дорожка CLI из кампании паритета: PowerShell не различает регистр ни в именах
параметров, ни в значениях [ValidateSet], argparse различает и в том и в
другом. Проверено на читающем навыке: cf-info -Mode BRIEF на PS отрабатывает,
на py падал.
ci_parse_args (эталон — meta-compile) вставлен в 68 py-портов, вызовы
parser.parse_args заменены. Навыков с DSL меньше трети, поэтому именно эта
правка делает паритет общим: *-info, *-validate, db-*, cfe-*, web-* тоже
принимают ввод так же, как их .ps1.
Реестр check-inline-drift дополнен списком потребителей — и сразу окупился:
поймал, что массовая замена переписала последнюю строку внутри самого
хелпера и копии стали рекурсивными.
Версии бампнуты синхронно в обоих портах всех 68 навыков.
Проверка: 673/673 (PS), 670+3 skipped (PY), гарды 4/4; паритет версий портов
проверен по заголовкам.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Порты расходились в трёх местах, и каждое проявлялось только в редком
случае — то есть там, где цена ошибки максимальна.
1. Вывод. PS наследовал консоль (текст платформы попадал в поток без
метки и в непредсказуемой позиции), PY захватывал его и не печатал
вовсе — аварийное сообщение мимо /Out терялось. Теперь оба порта
захватывают вывод и печатают его отдельным блоком «Вывод платформы»,
только если он непуст: молчащий успех остаётся молчаливым.
2. Кодировка. PS декодировал вывод ibcmd как cp866, тогда как ibcmd
пишет UTF-8 (проверено на 8.3.24, 8.3.27, 8.5) — русские сообщения
приходили крякозябрами. PY использовал text=True, то есть локальную
кодовую страницу. Теперь оба декодируют UTF-8 строго, с фолбэком на
cp866 для аварийного текста 1cv8.
3. Квотирование. PY не работал с путём к базе, содержащим пробел, — ни
на Windows, ни на macOS: 1С ждёт кавычки внутри значения
(File="путь"), а subprocess квотирует токен целиком. PS работал,
потому что вклеивал кавычки сам. Теперь обе версии строят токены
одинаково; на Windows PY передаёт готовую командную строку.
Плюс валидация ввода: путевые параметры прощают обрамляющие кавычки,
пробелы по краям и хвостовой разделитель, а навыки, требующие готовую
базу, проверяют её наличие до запуска.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
powershell.exe -File (именно так вызываются навыки) не умеет биндить
массив: значения через пробел уходят в позиционные параметры, а список
через запятую приезжает одним склеенным токеном. Поэтому параметр
принимает список в конвенции репозитория (как -Objects/-Files) и
разбирается внутри; нативный вызов массивом продолжает работать.
То же разбиение добавлено в py-порт — чтобы одна и та же строка вызова
работала в обоих. Значение с запятой внутри не поддерживается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Набор аргументов платформы был закрыт: общий ключ запуска (например
/UseHwLicenses+ на машине с аппаратной лицензией) передать было нельзя,
и сборка на автоматически созданной временной базе падала с «Не найдена
лицензия».
Добавлен escape hatch — по параметру на движок, плюс зеркальные ключи
в .v8-project.json (v8args / ibcmdargs) для машинно-специфичных флагов:
- -AdditionalV8Arguments → 1cv8.exe, ключи вида /Key
- -AdditionalIbcmdArguments → ibcmd, ключи вида --key=value
Аргументы уходят во все запуски платформы, которые делает навык:
epf-build без базы прогоняет CREATEINFOBASE, /LoadConfigFromFiles,
/UpdateDBCfg и саму сборку — ключ получает каждый.
До запуска отклоняются: аргумент, которым управляет сам скрипт (режим,
подключение, /Out, пакетная операция), позиционный токен для ibcmd и
параметр «не своего» движка. Значения секрето-опасных ключей (/P, /UC,
--password, --token) в логе маскируются.
Порядок источников: .v8-project.json, затем параметр. Поведение без
новых параметров не меняется.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Прежняя маскировка (^/N|/P по токену) на *nix цепляла путь, начинающийся с
заглавной /N или /P (напр. /Projects, /Numbers) — косметическая пере-маскировка
в строке Running. Заменено на редактуру конкретных значений (пароль/пользователь)
через литеральную замену: секрет скрывается везде, где встречается, а похожие на
флаг пути не трогаются. 11 навыков, оба порта.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Убрана спекулятивная гипотеза причины (headless/GUI/лицензия) из сообщения:
краш сигналом/exception может быть вызван чем угодно, а зашитая догадка
заякоривает модель-координатора на неверном диагнозе. Оставлены только факты
(сигнал/exception-код, признак аномального завершения), следствие (ИБ может
быть несогласованна) и нейтральное действие (verify before retrying).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Навыки печатали полную командную строку платформы, включая /P<пароль> (1cv8)
и --password= (ibcmd), в диагностику. Добавлен per-token маскер (/N, /P,
--user=, --password= → ***); привязка к началу токена не трогает пути.
Оба порта, обе ветки движка, все 9 навыков с параметрами подключения.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Мутирующие навыки не производят одиночный артефакт, но при крахе платформы
(нет GUI-сессии/лицензии) возвращали голый код вроде -11. Добавлен аннотатор:
POSIX-сигнал (напр. -11 → SIGSEGV) и Windows exception-код (напр. 0xC0000005) →
внятное сообщение с предупреждением о возможной несогласованности ИБ. Без
ожидания фоновых процессов и без ps-скрейпинга.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ibcmd при отсутствии/неверных учётных данных уходил в интерактивный запрос и
зависал. Эмпирически (реальный ibcmd, macOS+Windows, базы с пользователями):
вис возникает только когда не задан -UserName; stdin=DEVNULL НЕ лечит (наоборот
виснет). Лечит закрытый stdin-пайп (EOF) → ibcmd падает за ~4-7с с внятным
«Идентификация пользователя не выполнена».
- python (12 файлов + stub): run_ibcmd(cmd, has_username) c input="" вместо
прямого subprocess.run; без таймаута. Остаточный случай python+Windows+нет
-UserName (ibcmd читает консоль) помечается hint'ом в stderr (English, model-facing).
- PowerShell (12 файлов): Invoke-IbcmdProcess через [System.Diagnostics.Process] +
RedirectStandardInput + StandardInput.Close() → fast-fail; StandardOutputEncoding
cp866 (кириллица ibcmd не мойибейлится). Ветка 1cv8/DESIGNER не тронута.
- Жёсткий таймаут не вводим: у вызывающей стороны свой бэкстоп, авто-kill рвал бы
длинные легитимные операции.
- Синхронный бамп версий ps1+py всех затронутых навыков.
Fixes#28
Co-Authored-By: Korolev Pavel <korolev.vrn@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- subsystem-compile.py: авто-валидация через subprocess к subsystem-validate.py
(вместо powershell.exe + .ps1), без проброса кода возврата — паритет с сиблингами
- resolve_v8path (12 db/epf .py): резолв каталога платформы по ОС (1cv8 без .exe
на *nix), авто-поиск /opt/1cv8/*; ibcmd только явным путём, 1cv8c убран
- сообщения об ошибках платформо-нейтральные (ps1+py), коммент-заглушка PY-only в .ps1
- runner.mjs (v0.5): loadV8Context кросс-платформенный (1cv8/ibcmd без .exe)
- тесты: stdoutContains маркер валидатора (#26), негативный кейс db-update/error-bad-v8dir
- синхронный бамп версий ps1+py всех затронутых навыков
Fixes#26, #27
Co-Authored-By: Korolev Pavel <korolev.vrn@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ibcmd без --data использует общий каталог автономного сервера в
%LOCALAPPDATA%\standalone-server: он копит реестр/локи, и зависший
процесс (например, ушедший в консольный интерактив при нехватке кред)
держит его лок, блокируя ВСЕ последующие ibcmd-операции «Рабочий каталог
заблокирован процессом N».
Теперь каждая ibcmd-операция получает свой одноразовый --data=<temp> и
удаляет его после: зависший/параллельный вызов лочит только свой каталог,
не накапливается реестр («уже зарегистрирована»). База (--db-path) и
чужой автономный сервер на дефолтном каталоге не затрагиваются.
Реализация: ps1 — --data=$tempDir (создаётся до try, finally чистит на
exit); py — свой ib_data через tempfile.mkdtemp + atexit-очистка (надёжно
на любом sys.exit); stub-db-create — свой ib_data + явная очистка.
Проверено: A/B через навык с живым холдером — операция с общим --data
падает «заблокирован процессом N», навык (свой --data) проходит; очистка
temp-каталогов подтверждена (оба порта); 1cv8-ветки не затронуты.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Распространение пилота dt-пары на остальные применимые навыки. Если
-V8Path указывает на ibcmd.exe — операция идёт через автономный сервер
(offline, без запуска платформы), иначе как прежде через 1cv8 DESIGNER.
Маппинг (только файловые базы --db-path):
- db-create → infobase create --create-database [--restore=dt|--load=cf --apply]
- db-dump-cf → infobase config save
- db-load-cf → infobase config load
- db-update → infobase config apply --force (--force обязателен: без него
ibcmd уходит в интерактивный [y/n] и в неинтерактиве отменяет, exit 102)
- db-dump-xml→ infobase config export (иерархический, Mode Full/Changes)
- db-load-xml→ infobase config import (+цепочка config apply при -UpdateDB)
Несовместимое под ibcmd даёт понятную ошибку с указанием на 1cv8:
серверные базы, -AllExtensions, -Format Plain, Mode Partial/UpdateInfo,
Files/ListFile. 1cv8-ветки без изменений. Версии 1.1→1.2 (xml-load 1.5→1.6).
E2E цепочка через ibcmd: create→dump-cf→load-cf→update→dump-xml→
load-xml+UpdateDB — всё exit 0; 1cv8-регресс (create/load-cf/update) цел.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Новая лестница приоритетов поиска платформы (ps1 + py, 18 файлов):
1) -V8Path; 2) v8path из .v8-project.json (скрипт сам ищет файл вверх от
cwd — пин-версия соблюдается даже без -V8Path); 3) glob по Program Files
[+ (x86)] с ЧИСЛОВОЙ сортировкой версий и заметкой «Auto-selected
platform X.Y.Z: <путь>».
Исправляет: лексикографический выбор версии (8.3.9 вместо 8.3.27);
тихий выбор максимальной версии (риск подъёма формата базы) — теперь
реестр в приоритете; узкую область поиска (добавлен x86). Python-порт
деградирует без падения вне Windows (glob пуст → чистый exit, не
трейсбэк). Версии: cf-семейство 1.0→1.1, load-xml/load-git 1.4→1.5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Партиал-загрузка ParentConfigurations.bin платформой не принимается
(мутный отказ «редактирование объекта метаданных запрещено», пустое имя
= корень; смена поддержки требует полной загрузки). Чтобы не падать
невнятно, оба навыка теперь исключают служебные файлы поддержки из
partial-списка с явной подсказкой.
- db-load-xml (Mode Partial): фильтрует -Files и -ListFile — убирает
ParentConfigurations.bin и ConfigDumpInfo.xml, грузит остальное;
если после фильтра пусто — подсказка использовать -Mode Full.
- db-load-git: ParentConfigurations.bin из git-diff отбрасывается явно
(раньше — неявно, через несуществующий производный xml) и о смене
поддержки печатается предупреждение; ConfigDumpInfo.xml как и прежде
пропускается.
Не блокируем быструю частичную загрузку объектов (bin всё равно partial
не применяется — ничего «быстрого» не теряем); смену поддержки честно
направляем на полную загрузку. Оба порта, v1.3→v1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Platform writes load-time property/type/enum rejections ("Неверное
свойство объекта метаданных", "Неизвестное имя типа" и т.п.) into the
/Out log but still exits with code 0, silently dropping the offending
metadata. db-load-xml now parses the log for these patterns and prints
a yellow "[warning] N rejection(s)" block to stdout so users (and the
model) can see them immediately.
Exit code still mirrors the platform by default — we don't second-guess
its verdict. With the new -StrictLog switch, rejection patterns are
elevated to exit code 1, which is the mode verify-snapshots.mjs uses
for honest E2E verdicts. All three db-load-xml call sites in the
verifier (main config, CFE base, CFE extension) now pass -StrictLog.
Found while investigating upload/form-baseline/gotchas.md #4 where AR
attribute emission was wrong but verify-snapshots showed green because
the old exit-code-only check missed the silent drops.
Bumped db-load-xml.ps1 + .py to v1.3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes#8 — adds /UpdateDBCfg parameter support so users can load config
and update database in a single 1C Designer invocation instead of two.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without reconfiguring stderr, Cyrillic error messages appear garbled
on Windows (cp1251 default). Mirrors the existing stdout fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python subprocess.run() with list args handles quoting automatically.
Extra f'"{path}"' wrapping made quotes literal, breaking 1cv8.exe CLI.
Removed inner quotes from all 8 db-* scripts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python on Windows defaults to cp1251 for piped stdout, which cannot
handle Unicode box-drawing characters used in info/analysis output.
Added sys.stdout.reconfigure(encoding="utf-8") to all 59 Python scripts.
Tested on real config data: epf-init, epf-validate, cf-info, cf-validate,
meta-info, form-info, role-info, skd-info, subsystem-info — all passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cross-platform Python alternatives alongside existing PowerShell
scripts. PS1 remains the default runtime; Python is opt-in via switch
scripts. All parameters are identical between runtimes.
New files:
- 58 Python scripts in .claude/skills/*/scripts/*.py
- scripts/switch-to-python.py and switch-to-powershell.py
- docs/python-porting-guide.md
- __pycache__/ added to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>