diff --git a/.claude/commands/new-project.md b/.claude/commands/new-project.md index 5e03f27..6fb889a 100644 --- a/.claude/commands/new-project.md +++ b/.claude/commands/new-project.md @@ -1,14 +1,31 @@ -Помоги инициализировать новый проект из этого шаблона. - -Спроси у пользователя: -1. Название проекта -2. Краткое описание (1-2 предложения) -3. Стек (предложи варианты из CLAUDE.md Section 6) -4. Путь для нового проекта (по умолчанию: D:\code\2026\<название>) - -Затем: -1. Скопируй шаблон в новую папку (без video_output/, .git/, node_modules/) -2. Заполни PROJECT_CONTEXT.md данными от пользователя -3. Обнови DEV_CONTEXT.md — очисти историю, установи этап "Инициализация" -4. Выполни `git init` и первый коммит `init: project scaffold` -5. Выведи итог: что создано, следующий шаг +Инициализация нового проекта на базе шаблона Cortex. + +Эта команда автоматизирует создание нового репозитория с сохранением всей инфраструктуры Cortex (хуки, воркфлоу, команды), но с очищенным контентом. + +### Процесс +1. Спроси у пользователя: + - **Название проекта** + - **Описание** (цель проекта) + - **Стек** (предложи варианты: Python/FastAPI, Next.js/TypeScript, Node.js/Express) + - **Путь** (по умолчанию: D:/code/2026/<название>) + +2. Выполни скрипт инициализации: + ```bash + uv run --project tools/scaffold python tools/scaffold/main.py \ + --name "<название>" \ + --description "<описание>" \ + --stack "<стек>" \ + --target "<путь>" + ``` + +3. После успешного завершения: + - Сообщи пользователю, что проект создан. + - Дай инструкции по переходу в новую папку и запуску `claude`. + - Напомни заполнить `PROJECT_CONTEXT.md` в новом проекте. + +### Что будет сделано +- Копирование структуры `.claude/`, `.github/`, `tools/`, `docs/`. +- Очистка `PROJECT_CONTEXT.md` и `DEV_CONTEXT.md` (замена на шаблоны). +- Создание чистого `README.md`. +- Удаление Cortex-специфичных данных и самого инструмента `tools/scaffold` из целевой папки. +- Инициализация Git и первый коммит. diff --git a/tools/scaffold/main.py b/tools/scaffold/main.py new file mode 100644 index 0000000..3881c99 --- /dev/null +++ b/tools/scaffold/main.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +"""Cortex Scaffolder — Create a new project from the Cortex template. +""" +from __future__ import annotations + +import argparse +import os +import shutil +import subprocess +import sys +from datetime import datetime + +from beartype import beartype + +PROJECT_CONTEXT_TEMPLATE = """# PROJECT_CONTEXT + +## Проект +- Название: {name} +- Цель: {description} + +## Стек +- Orchestration: Claude Code CLI +- Agents: Jules, Codex +- PM: GitHub Issues +- Infrastructure: GitHub Actions +- Stack details: {stack} + +## Архитектура +(Describe your architecture here) + +## Этапы +1. [ ] Инициализация проекта +2. [ ] Определение базовой архитектуры + +## Definition of Done +- [ ] Фича протестирована +- [ ] PR создан и подтверждён +""" + +DEV_CONTEXT_TEMPLATE = """# Development Context Log + +## Последнее обновление +- Дата: {date} + +## Текущий статус +- Этап: Инициализация +- Последнее действие: Проект создан из Cortex шаблона +- Следующий шаг: Настроить PROJECT_CONTEXT.md + +## История изменений + +### {date} — Инициализация +- Что сделано: + - Проект развернут из шаблона Cortex +- Решения: Использование Cortex для оркестрации AI-агентов. + +## Технические детали +- Архитектура: +- Стек: {stack} + +## Прогресс +- [x] Инициализация из шаблона +- [ ] Настроить PROJECT_CONTEXT.md +""" + +README_TEMPLATE = """# {name} + +{description} + +--- + +## 🚀 Quick Start + +1. **Start the engine**: + Launch [Claude Code](https://claude.ai/code) in the project root. +2. **Run your first Council**: + ```bash + /council + ``` +3. **Dispatch tasks**: + ```bash + /dispatch + ``` + +--- + +## 🛠 Features (Powered by Cortex) +- Multi-agent orchestration via GitHub Issues. +- Automated trend scanning with `/heartbeat`. +- Built-in safety hooks and CI/CD workflows. + +## 📝 License +This project is open-source. +""" + +@beartype +def ignore_files(path: str, names: list[str]) -> list[str]: + ignored = { + ".git", + "node_modules", + "__pycache__", + ".venv", + ".env", + "video_output", + "init-project.sh", + } + to_ignore = [ + name for name in names + if name in ignored + or name.endswith(".pyc") + or name.endswith(".log") + or (name.endswith(".json") and name not in {".mcp.json", "settings.json"}) + ] + + # Specifically ignore tools/scaffold to keep the template clean + if os.path.basename(path) == "tools" and "scaffold" in names: + to_ignore.append("scaffold") + + return to_ignore + +@beartype +def scaffold_project( + name: str, + description: str, + stack: str, + target_dir: str, + source_dir: str +) -> None: + if os.path.exists(target_dir): + print(f"Error: Directory {target_dir} already exists.") + sys.exit(1) + + print(f"Creating project '{name}' in {target_dir}...") + + # Use absolute paths for source and target + abs_source = os.path.abspath(source_dir) + abs_target = os.path.abspath(target_dir) + + shutil.copytree(abs_source, abs_target, ignore=ignore_files) + + current_date = datetime.now().strftime("%Y-%m-%d") + + # Overwrite context files + with open(os.path.join(abs_target, "PROJECT_CONTEXT.md"), "w", encoding="utf-8") as f: + f.write(PROJECT_CONTEXT_TEMPLATE.format(name=name, description=description, stack=stack)) + + with open(os.path.join(abs_target, "DEV_CONTEXT.md"), "w", encoding="utf-8") as f: + f.write(DEV_CONTEXT_TEMPLATE.format(date=current_date, stack=stack)) + + with open(os.path.join(abs_target, "README.md"), "w", encoding="utf-8") as f: + f.write(README_TEMPLATE.format(name=name, description=description)) + + # Init git + try: + # Check if git is available + subprocess.run(["git", "--version"], capture_output=True, check=True) + + subprocess.run(["git", "init"], cwd=abs_target, check=True) + subprocess.run(["git", "add", "."], cwd=abs_target, check=True) + subprocess.run(["git", "commit", "-m", "init: project scaffold from Cortex template"], cwd=abs_target, check=True) + print("Git repository initialized.") + except (subprocess.CalledProcessError, FileNotFoundError) as e: + print(f"Warning: Failed to initialize git repository: {e}") + +@beartype +def main() -> None: + parser = argparse.ArgumentParser(description="Cortex Scaffolder") + parser.add_argument("--name", required=True, help="Project name") + parser.add_argument("--description", required=True, help="Project description") + parser.add_argument("--stack", required=True, help="Project stack") + parser.add_argument("--target", required=True, help="Target directory") + parser.add_argument("--source", default=".", help="Source template directory") + + args = parser.parse_args() + + scaffold_project( + name=args.name, + description=args.description, + stack=args.stack, + target_dir=args.target, + source_dir=args.source + ) + print(f"Project '{args.name}' successfully created.") + +if __name__ == "__main__": + main() diff --git a/tools/scaffold/pyproject.toml b/tools/scaffold/pyproject.toml new file mode 100644 index 0000000..e64217b --- /dev/null +++ b/tools/scaffold/pyproject.toml @@ -0,0 +1,16 @@ +[project] +name = "cortex-scaffold" +version = "0.1.0" +requires-python = ">=3.12" +dependencies = [ + "beartype>=0.18.0", +] + +[dependency-groups] +dev = [ + "pyright>=1.1.396", +] + +[tool.pyright] +typeCheckingMode = "strict" +pythonVersion = "3.12" diff --git a/tools/scaffold/uv.lock b/tools/scaffold/uv.lock new file mode 100644 index 0000000..5f47df1 --- /dev/null +++ b/tools/scaffold/uv.lock @@ -0,0 +1,62 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "cortex-scaffold" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "beartype" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pyright" }, +] + +[package.metadata] +requires-dist = [{ name = "beartype", specifier = ">=0.18.0" }] + +[package.metadata.requires-dev] +dev = [{ name = "pyright", specifier = ">=1.1.396" }] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "pyright" +version = "1.1.408" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +]