diff --git a/README.md b/README.md index 0dce8fe..af41d7b 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,14 @@ nightshift run --task TASK-001 For the first real-model tutorial target: ```bash -nightshift init --template imageboard --root nightshift-imageboard +nightshift init --template tutorial-imageboard --root nightshift-imageboard +``` + +Other built-in real-model templates: + +```bash +nightshift init --template real-simple --root bookmarks-demo +nightshift init --template real-long-running --root incident-service ``` Open the read-only artifact dashboard: diff --git a/examples/tutorial/01-imageboard/README.md b/examples/tutorial/01-imageboard/README.md index 115ffdf..5b4dcc6 100644 --- a/examples/tutorial/01-imageboard/README.md +++ b/examples/tutorial/01-imageboard/README.md @@ -9,7 +9,7 @@ The target is a compact 4chan-style imageboard: boards, threads, replies, images Run this from a disposable parent directory: ```bash -nightshift init --template imageboard --root nightshift-imageboard +nightshift init --template tutorial-imageboard --root nightshift-imageboard cd nightshift-imageboard ``` diff --git a/nightshift/cli.py b/nightshift/cli.py index ed44730..417aa80 100644 --- a/nightshift/cli.py +++ b/nightshift/cli.py @@ -8,7 +8,7 @@ import sys from .config import validate_config from .errors import NightShiftError -from .init import init_project +from .init import available_templates, init_project from .pipeline import PipelineRunner from .runlog import RunLogger from .status import build_status, format_status @@ -33,7 +33,7 @@ def build_parser() -> argparse.ArgumentParser: init_parser.add_argument( "--template", default="basic", - choices=("basic", "imageboard"), + choices=available_templates(), help="Starter template to create.", ) init_parser.add_argument("--force", action="store_true", help="Overwrite existing starter files.") diff --git a/nightshift/init.py b/nightshift/init.py index d33a190..3c3f4b0 100644 --- a/nightshift/init.py +++ b/nightshift/init.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path +import shutil from .errors import InitError from . import templates @@ -32,9 +33,18 @@ IMAGEBOARD_FILES = { PROJECT_TEMPLATES = { "basic": STARTER_FILES, - "imageboard": IMAGEBOARD_FILES, + "tutorial-imageboard": IMAGEBOARD_FILES, } +TEMPLATE_ROOT = Path(__file__).resolve().parent / "project_templates" + + +def available_templates() -> tuple[str, ...]: + names = set(PROJECT_TEMPLATES) + if TEMPLATE_ROOT.exists(): + names.update(path.name for path in TEMPLATE_ROOT.iterdir() if path.is_dir()) + return tuple(sorted(names)) + def init_project(root: Path, force: bool = False, template: str = "basic") -> list[Path]: """Create starter NightShift files under root. @@ -43,10 +53,11 @@ def init_project(root: Path, force: bool = False, template: str = "basic") -> li """ root = root.resolve() - if template not in PROJECT_TEMPLATES: - known = ", ".join(sorted(PROJECT_TEMPLATES)) + if template not in available_templates(): + known = ", ".join(available_templates()) raise InitError(f"Unknown template '{template}'. Available templates: {known}") - files = PROJECT_TEMPLATES[template] + template_dir = TEMPLATE_ROOT / template + files = _template_files(template, template_dir) targets = [root / relative for relative in files] existing = [path for path in targets if path.exists()] if existing and not force: @@ -60,7 +71,21 @@ def init_project(root: Path, force: bool = False, template: str = "basic") -> li for relative, content in files.items(): path = root / relative path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content, encoding="utf-8") + if content is None: + source = template_dir / relative + shutil.copyfile(source, path) + else: + path.write_text(content, encoding="utf-8") written.append(path) return written + + +def _template_files(template: str, template_dir: Path) -> dict[str, str | None]: + if template_dir.exists(): + return { + path.relative_to(template_dir).as_posix(): None + for path in sorted(template_dir.rglob("*")) + if path.is_file() + } + return PROJECT_TEMPLATES[template] diff --git a/nightshift/project_templates/basic/agents/implementer.md b/nightshift/project_templates/basic/agents/implementer.md new file mode 100644 index 0000000..ea9b27b --- /dev/null +++ b/nightshift/project_templates/basic/agents/implementer.md @@ -0,0 +1,11 @@ +# Implementer + +You are the implementation agent for NightShift. + +Implement the approved plan inside the scoped project directory. + +Rules: +- Make the smallest correct change. +- Do not edit files outside scope. +- Preserve existing style. +- Write useful implementation notes. diff --git a/nightshift/project_templates/basic/agents/planner.md b/nightshift/project_templates/basic/agents/planner.md new file mode 100644 index 0000000..da3300c --- /dev/null +++ b/nightshift/project_templates/basic/agents/planner.md @@ -0,0 +1,13 @@ +# Planner + +You are the planning agent for NightShift. + +Create a conservative implementation plan for one coding task. + +Rules: +- Do not write code. +- Identify relevant files. +- Preserve existing behavior. +- Prefer small changes. +- Include test strategy. +- Include risks. diff --git a/nightshift/project_templates/basic/agents/reviewer.md b/nightshift/project_templates/basic/agents/reviewer.md new file mode 100644 index 0000000..6aae61d --- /dev/null +++ b/nightshift/project_templates/basic/agents/reviewer.md @@ -0,0 +1,12 @@ +# Reviewer + +You are the review agent for NightShift. + +Decide whether the current task should pass, retry implementation, retry planning, or fail. + +Output exactly: + +status: pass | fail | retry | escalate +reason: +next_stage: +context_update: diff --git a/nightshift/project_templates/basic/nightshift.yaml b/nightshift/project_templates/basic/nightshift.yaml new file mode 100644 index 0000000..2241055 --- /dev/null +++ b/nightshift/project_templates/basic/nightshift.yaml @@ -0,0 +1,67 @@ +project: + name: example-project + root: . + task_file: tasks.md + artifact_dir: .nightshift + +safety: + require_clean_worktree: false + scoped_paths: + - . + allowed_commands: + - python -m unittest + forbidden_commands: + - rm -rf + - git push + - curl | bash + +agents: + planner: + backend: command + command: echo + system_prompt: agents/planner.md + + implementer: + backend: command + command: echo + system_prompt: agents/implementer.md + + reviewer: + backend: command + command: echo + system_prompt: agents/reviewer.md + +pipeline: + max_task_retries: 3 + stages: + - id: plan + type: agent + agent: planner + output: plan.md + + - id: review_plan + type: agent_review + agent: reviewer + on_fail: plan + output: plan-review.md + + - id: implement + type: agent + agent: implementer + output: implementation-log.md + + - id: test + type: command + commands: + - python -m unittest + output: test-output.txt + + - id: review + type: agent_review + agent: reviewer + on_fail: implement + output: review.md + + - id: summarize + type: summarize + output: final-notes.md diff --git a/nightshift/project_templates/basic/tasks.md b/nightshift/project_templates/basic/tasks.md new file mode 100644 index 0000000..5f77048 --- /dev/null +++ b/nightshift/project_templates/basic/tasks.md @@ -0,0 +1,10 @@ +# Tasks + +- [ ] TASK-001: Add your first NightShift task + +Description: +Describe the coding task NightShift should work on. + +Acceptance Criteria: +- The expected behavior is clear +- The task can be reviewed from generated artifacts diff --git a/nightshift/project_templates/real-long-running/.nightshift/agents/architect.md b/nightshift/project_templates/real-long-running/.nightshift/agents/architect.md new file mode 100644 index 0000000..9739e7d --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/agents/architect.md @@ -0,0 +1,7 @@ +You are the architecture agent for NightShift. + +Use the plan, task, and context to produce a short implementation design. +Focus on module boundaries, data model, route responsibilities, and test seams. + +Do not write code. +Do not request broad unrelated context. diff --git a/nightshift/project_templates/real-long-running/.nightshift/agents/junior.md b/nightshift/project_templates/real-long-running/.nightshift/agents/junior.md new file mode 100644 index 0000000..d9700cb --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/agents/junior.md @@ -0,0 +1,12 @@ +You are the junior implementation agent for NightShift. + +Implement the task from the plan and architecture notes. + +Output only complete file content blocks: +```file:relative/path.py + +``` + +Keep the implementation simple and testable. +Include tests. +Do not include explanations outside file blocks. diff --git a/nightshift/project_templates/real-long-running/.nightshift/agents/planner.md b/nightshift/project_templates/real-long-running/.nightshift/agents/planner.md new file mode 100644 index 0000000..40449c4 --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/agents/planner.md @@ -0,0 +1,21 @@ +You are the planning agent for NightShift. + +Create a concise, realistic implementation plan for the current task. +Request repository context when needed. + +Use lookup requests exactly: + +lookup_requests: +- tool: read_file + path: relative/path.py +- tool: grep + path: . + pattern: search_regex + +After context is available, write: +- implementation steps +- files to create or edit +- test strategy +- risks and sequencing notes + +Do not write code. diff --git a/nightshift/project_templates/real-long-running/.nightshift/agents/reviewer.md b/nightshift/project_templates/real-long-running/.nightshift/agents/reviewer.md new file mode 100644 index 0000000..3f7f402 --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/agents/reviewer.md @@ -0,0 +1,18 @@ +You are the review agent for NightShift. + +Review the task, plan, architecture notes, patch artifacts, test output, and final repository state. + +Output exactly: + +status: pass | fail | retry | escalate +reason: +next_stage: +context_update: + +For the junior review: +- If the implementation satisfies the task, output `status: pass` and `next_stage: summarize`. +- If the implementation is close but flawed, output `status: retry` and `next_stage: implement_senior`. + +For the senior review: +- Use retry only for fixable senior issues. +- Use pass only when acceptance criteria are satisfied. diff --git a/nightshift/project_templates/real-long-running/.nightshift/agents/senior.md b/nightshift/project_templates/real-long-running/.nightshift/agents/senior.md new file mode 100644 index 0000000..1b99773 --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/agents/senior.md @@ -0,0 +1,13 @@ +You are the senior implementation agent for NightShift. + +You receive the previous junior attempt, validation errors, test output, review notes, and repository context. +Produce a corrected implementation for the same task. + +Output only complete file content blocks: +```file:relative/path.py + +``` + +Prioritize correctness, coherent design, and passing tests. +Preserve useful work from the junior attempt when it is sound. +Do not include explanations outside file blocks. diff --git a/nightshift/project_templates/real-long-running/.nightshift/tasks.md b/nightshift/project_templates/real-long-running/.nightshift/tasks.md new file mode 100644 index 0000000..83aee35 --- /dev/null +++ b/nightshift/project_templates/real-long-running/.nightshift/tasks.md @@ -0,0 +1,40 @@ +# Tasks + +- [ ] TASK-001: Incident intake service foundation + +Description: +Create a Flask service for incident intake and triage. Implement SQLite schema, app factory, incident model helpers, routes for creating/listing/detailing incidents, and tests. This is intentionally larger than the simple template and should exercise planning, architecture notes, implementation, tests, and review. + +Acceptance Criteria: +- Provides app factory and package layout under `src/` +- Defines SQLite schema for incidents, status, severity, and audit events +- Implements create, list, and detail routes +- Records audit events when incidents are created or status changes +- Includes pytest tests with temporary database setup + +- [ ] TASK-002: Assignment and status workflow + +Dependencies: +- TASK-001 + +Description: +Add assignee tracking, status transitions, validation rules, and audit history views. + +Acceptance Criteria: +- Supports assigning incidents to owners +- Validates allowed status transitions +- Stores audit events for assignment and status changes +- Includes route and model tests + +- [ ] TASK-003: Search and reporting + +Dependencies: +- TASK-002 + +Description: +Add filtering by severity/status/assignee and a lightweight CSV export for open incidents. + +Acceptance Criteria: +- Supports filtered incident list queries +- Exports open incidents as CSV +- Includes tests for filter combinations and export content diff --git a/nightshift/project_templates/real-long-running/README.md b/nightshift/project_templates/real-long-running/README.md new file mode 100644 index 0000000..0e44ee6 --- /dev/null +++ b/nightshift/project_templates/real-long-running/README.md @@ -0,0 +1,29 @@ +# Real Long-Running Template: Junior/Senior Pipeline + +Use this template for a realistic, longer NightShift run where several agents cooperate on a non-trivial service task. + +```bash +nightshift init --template real-long-running --root incident-service +cd incident-service +python -m pip install flask pytest +nightshift run --task TASK-001 +``` + +Use case: build and evolve a small incident intake and triage service with Flask, SQLite, tests, and audit history. + +Agents: + +- Planner: `qwen2.5-coder:14b` +- Architect: `qwen2.5-coder:14b` +- Junior implementer: `qwen2.5-coder:14b` +- Senior implementer: `qwen3-coder:30b` +- Reviewer: `qwen2.5-coder:14b` + +Junior/senior routing: + +- The junior implementer tries first. +- Patch validation, patch apply, or tests can route directly to `implement_senior`. +- The junior reviewer prompt asks the reviewer to jump to `summarize` when the junior passes, or to `implement_senior` when the junior needs escalation. +- The senior path then writes its own patch artifacts, applies, tests, and reviews. + +This template intentionally has a longer pipeline and a bigger blast radius. Use it after the simple template works on your machine. diff --git a/nightshift/project_templates/real-long-running/docs/.gitkeep b/nightshift/project_templates/real-long-running/docs/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/real-long-running/docs/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/real-long-running/nightshift.yaml b/nightshift/project_templates/real-long-running/nightshift.yaml new file mode 100644 index 0000000..a4b1c6d --- /dev/null +++ b/nightshift/project_templates/real-long-running/nightshift.yaml @@ -0,0 +1,151 @@ +project: + name: service-maintenance + root: . + task_file: .nightshift/tasks.md + artifact_dir: .nightshift + +safety: + require_clean_worktree: false + scoped_paths: + - src + - tests + - docs + - pyproject.toml + - README.md + allowed_commands: + - python -m pytest -q + forbidden_commands: + - rm -rf + - git push + - curl | bash + +experiment: + label: long-running-junior-senior + prompt_variant: qwen25-junior-qwen3-senior-v1 + +agents: + planner: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.2 + system_prompt: .nightshift/agents/planner.md + + architect: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.2 + system_prompt: .nightshift/agents/architect.md + + junior: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/junior.md + + senior: + backend: ollama + model: qwen3-coder:30b + temperature: 0.1 + system_prompt: .nightshift/agents/senior.md + + reviewer: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/reviewer.md + +pipeline: + max_task_retries: 4 + continue_on_task_failure: false + stages: + - id: plan + type: agent + agent: planner + output: plan.md + + - id: architecture + type: agent + agent: architect + output: architecture.md + + - id: context + type: repo_context + output: context-pack.md + + - id: implement_junior + type: file_writer + agent: junior + output: proposed.patch + + - id: normalize_junior + type: patch_normalizer + output: normalized.patch + + - id: validate_junior + type: patch_validator + output: patch-validation.md + max_files: 16 + max_lines: 1400 + on_fail: implement_senior + + - id: apply_junior + type: patch_apply + mode: apply + output: patch-apply-output.txt + on_fail: implement_senior + + - id: test_junior + type: command + commands: + - python -m pytest -q + output: test-output.txt + shell: true + timeout_seconds: 45 + on_fail: implement_senior + + - id: review_junior + type: agent_review + agent: reviewer + output: review.md + on_fail: implement_senior + + - id: implement_senior + type: file_writer + agent: senior + output: senior-proposed.patch + + - id: normalize_senior + type: patch_normalizer + output: senior-normalized.patch + + - id: validate_senior + type: patch_validator + output: senior-patch-validation.md + max_files: 20 + max_lines: 1800 + on_fail: implement_senior + + - id: apply_senior + type: patch_apply + mode: apply + output: senior-patch-apply-output.txt + on_fail: implement_senior + + - id: test_senior + type: command + commands: + - python -m pytest -q + output: senior-test-output.txt + shell: true + timeout_seconds: 60 + on_fail: implement_senior + + - id: review_senior + type: agent_review + agent: reviewer + output: senior-review.md + on_fail: implement_senior + + - id: summarize + type: summarize + output: final-notes.md diff --git a/nightshift/project_templates/real-long-running/src/incident_service/.gitkeep b/nightshift/project_templates/real-long-running/src/incident_service/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/real-long-running/src/incident_service/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/real-long-running/tests/.gitkeep b/nightshift/project_templates/real-long-running/tests/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/real-long-running/tests/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/real-simple/.nightshift/agents/implementer.md b/nightshift/project_templates/real-simple/.nightshift/agents/implementer.md new file mode 100644 index 0000000..dda6cbc --- /dev/null +++ b/nightshift/project_templates/real-simple/.nightshift/agents/implementer.md @@ -0,0 +1,12 @@ +You are the implementation agent for NightShift. + +Output only complete file content blocks. +Use one fenced block per changed file: +```file:relative/path.py + +``` + +Keep the change scoped to the current task. +Prefer straightforward Flask and sqlite3 code. +Include pytest tests when needed. +Do not include explanations outside file blocks. diff --git a/nightshift/project_templates/real-simple/.nightshift/agents/planner.md b/nightshift/project_templates/real-simple/.nightshift/agents/planner.md new file mode 100644 index 0000000..278aae2 --- /dev/null +++ b/nightshift/project_templates/real-simple/.nightshift/agents/planner.md @@ -0,0 +1,19 @@ +You are the planning agent for NightShift. + +Create a concise implementation plan for the current task. + +If repository context is needed, output lookup requests exactly: + +lookup_requests: +- tool: read_file + path: relative/path.py +- tool: grep + path: . + pattern: search_regex + +After context is available, write: +- files to create or edit +- tests to add +- risks + +Do not write code. diff --git a/nightshift/project_templates/real-simple/.nightshift/agents/reviewer.md b/nightshift/project_templates/real-simple/.nightshift/agents/reviewer.md new file mode 100644 index 0000000..d1ae8ae --- /dev/null +++ b/nightshift/project_templates/real-simple/.nightshift/agents/reviewer.md @@ -0,0 +1,13 @@ +You are the review agent for NightShift. + +Review the task, plan, patch artifacts, test output, and final repository state. + +Output exactly: + +status: pass | fail | retry | escalate +reason: +next_stage: +context_update: + +Use retry for fixable implementation or test failures. +Use pass only when acceptance criteria are satisfied. diff --git a/nightshift/project_templates/real-simple/.nightshift/tasks.md b/nightshift/project_templates/real-simple/.nightshift/tasks.md new file mode 100644 index 0000000..c7d4428 --- /dev/null +++ b/nightshift/project_templates/real-simple/.nightshift/tasks.md @@ -0,0 +1,41 @@ +# Tasks + +- [ ] TASK-001: Bookmark API foundation + +Description: +Create a small Flask bookmark API with SQLite persistence. Implement app factory, schema initialization, bookmark model helpers, and routes to create, list, update, and delete bookmarks. Keep code under `src/` and tests under `tests/`. + +Acceptance Criteria: +- Provides an app factory under `src/` +- Initializes a SQLite schema for bookmarks +- Supports create, list, update, and delete routes +- Validates required URL/title fields +- Includes pytest tests using a temporary database + +- [ ] TASK-002: Tags and filtering + +Dependencies: +- TASK-001 + +Description: +Add tags to bookmarks and support filtering bookmarks by tag. + +Acceptance Criteria: +- Stores tags in SQLite +- Supports assigning multiple tags to a bookmark +- Supports filtering list output by tag +- Includes model and route tests + +- [ ] TASK-003: Import/export + +Dependencies: +- TASK-002 + +Description: +Add JSON import and export endpoints for bookmark backups. + +Acceptance Criteria: +- Exports all bookmarks and tags as JSON +- Imports valid JSON backup payloads +- Rejects malformed imports without corrupting existing data +- Includes tests for export and import behavior diff --git a/nightshift/project_templates/real-simple/README.md b/nightshift/project_templates/real-simple/README.md new file mode 100644 index 0000000..4a736d0 --- /dev/null +++ b/nightshift/project_templates/real-simple/README.md @@ -0,0 +1,14 @@ +# Real Simple Template: Bookmark API + +Use this template for a short, practical model-backed NightShift run. + +```bash +nightshift init --template real-simple --root bookmarks-demo +cd bookmarks-demo +python -m pip install flask pytest +nightshift run --task TASK-001 +``` + +The model is `qwen2.5-coder:14b` for planning, implementation, and review. The target is intentionally modest: a Flask + SQLite bookmark API with pytest coverage. + +NightShift files live in `.nightshift/`. Application code should be created under `src/`, and tests under `tests/`. diff --git a/nightshift/project_templates/real-simple/nightshift.yaml b/nightshift/project_templates/real-simple/nightshift.yaml new file mode 100644 index 0000000..bab18b6 --- /dev/null +++ b/nightshift/project_templates/real-simple/nightshift.yaml @@ -0,0 +1,96 @@ +project: + name: simple-bookmarks + root: . + task_file: .nightshift/tasks.md + artifact_dir: .nightshift + +safety: + require_clean_worktree: false + scoped_paths: + - src + - tests + - pyproject.toml + - README.md + allowed_commands: + - python -m pytest -q + forbidden_commands: + - rm -rf + - git push + - curl | bash + +experiment: + label: real-simple-qwen25 + prompt_variant: qwen2.5-coder-14b-file-writer-v1 + +agents: + planner: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.2 + system_prompt: .nightshift/agents/planner.md + + implementer: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/implementer.md + + reviewer: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/reviewer.md + +pipeline: + max_task_retries: 2 + continue_on_task_failure: false + stages: + - id: plan + type: agent + agent: planner + output: plan.md + + - id: context + type: repo_context + output: context-pack.md + + - id: implement + type: file_writer + agent: implementer + output: proposed.patch + + - id: normalize + type: patch_normalizer + output: normalized.patch + + - id: validate_patch + type: patch_validator + output: patch-validation.md + max_files: 8 + max_lines: 700 + on_fail: implement + + - id: apply_patch + type: patch_apply + mode: apply + output: patch-apply-output.txt + on_fail: implement + + - id: test + type: command + commands: + - python -m pytest -q + output: test-output.txt + shell: true + timeout_seconds: 20 + on_fail: implement + + - id: review + type: agent_review + agent: reviewer + output: review.md + on_fail: implement + + - id: summarize + type: summarize + output: final-notes.md diff --git a/nightshift/project_templates/real-simple/src/bookmarks/.gitkeep b/nightshift/project_templates/real-simple/src/bookmarks/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/real-simple/src/bookmarks/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/real-simple/tests/.gitkeep b/nightshift/project_templates/real-simple/tests/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/real-simple/tests/.gitkeep @@ -0,0 +1 @@ + diff --git a/agents/implementer.md b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/implementer.md similarity index 67% rename from agents/implementer.md rename to nightshift/project_templates/tutorial-imageboard/.nightshift/agents/implementer.md index 74dc3fe..462a843 100644 --- a/agents/implementer.md +++ b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/implementer.md @@ -1,13 +1,11 @@ You are the implementation agent for NightShift. Output only complete file content blocks. -Use one fenced block per changed file: - +Use one fenced block per file with this exact opening form: ```file:relative/path.py ``` - -Do not include explanations before or after the patch. +Do not include explanations before or after the file blocks. Include tests when needed. Keep the change as small as possible. Only edit files needed for the task. diff --git a/agents/planner.md b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/planner.md similarity index 95% rename from agents/planner.md rename to nightshift/project_templates/tutorial-imageboard/.nightshift/agents/planner.md index 2f3bbf3..00670c0 100644 --- a/agents/planner.md +++ b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/planner.md @@ -16,4 +16,4 @@ After context is provided, write a short plan with: - tests to add or update - risks -Do not write code. \ No newline at end of file +Do not write code. diff --git a/agents/reviewer.md b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/reviewer.md similarity index 87% rename from agents/reviewer.md rename to nightshift/project_templates/tutorial-imageboard/.nightshift/agents/reviewer.md index c520b48..4309b37 100644 --- a/agents/reviewer.md +++ b/nightshift/project_templates/tutorial-imageboard/.nightshift/agents/reviewer.md @@ -11,4 +11,4 @@ context_update: Use retry when the implementation is close but needs another patch. Use fail when the patch is unsafe, unrelated, or clearly broken. -Use pass only when the acceptance criteria are satisfied. \ No newline at end of file +Use pass only when the acceptance criteria are satisfied. diff --git a/nightshift/project_templates/tutorial-imageboard/.nightshift/tasks.md b/nightshift/project_templates/tutorial-imageboard/.nightshift/tasks.md new file mode 100644 index 0000000..0921b0f --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/.nightshift/tasks.md @@ -0,0 +1,72 @@ +# Tasks + +- [ ] TASK-001: Board and thread foundation + +Description: +Create the initial Flask imageboard application. Implement the board and thread data model, SQLite schema, model helpers, `/board/` and `/thread/` routes, and tests. Keep source code under `src/`, tests under `tests/`, HTML templates under `templates/`, and static files under `static/`. + +Acceptance Criteria: +- Defines SQLite tables for boards, threads, and replies +- Provides database initialization and model helper functions +- Implements `/board/` route showing threads for that board +- Implements `/thread/` route showing the thread and replies +- Includes route and model tests using a temporary database + +- [ ] TASK-002: Image upload and thumbnails + +Dependencies: +- TASK-001 + +Description: +Add image attachment support for new threads and replies. Store uploaded image metadata in SQLite, save uploaded files under `static/uploads`, and generate thumbnails under `static/thumbs`. + +Acceptance Criteria: +- Accepts image uploads for threads and replies +- Stores image filename, thumbnail filename, MIME type, and size +- Generates thumbnails with Pillow +- Rejects unsupported or oversized files +- Includes upload and thumbnail tests + +- [ ] TASK-003: Bump ordering and reply counts + +Dependencies: +- TASK-002 + +Description: +Sort board threads by most recent bump. Creating a reply updates the thread bump timestamp and increments reply counters. + +Acceptance Criteria: +- Board pages sort threads by latest bump time +- Replies increment thread reply count +- Reply creation updates bump timestamp +- Tests cover ordering and counters + +- [ ] TASK-004: Tripcodes and session cookies + +Dependencies: +- TASK-003 + +Description: +Add anonymous names, optional tripcodes, and a session cookie for lightweight poster identity. + +Acceptance Criteria: +- Supports optional name and tripcode input +- Stores tripcode hashes without storing raw tripcode secrets +- Sets and reuses a poster session cookie +- Displays stable poster identity on posts +- Includes tripcode and session tests + +- [ ] TASK-005: Moderation and report queue + +Dependencies: +- TASK-004 + +Description: +Add post reporting and a simple moderation queue. Moderators can view reports, dismiss reports, and hide reported posts. + +Acceptance Criteria: +- Users can report threads and replies +- Reports are stored with reason and timestamp +- Moderation queue lists open reports +- Moderation actions can dismiss reports or hide posts +- Includes moderation and report queue tests diff --git a/nightshift/project_templates/tutorial-imageboard/README.md b/nightshift/project_templates/tutorial-imageboard/README.md new file mode 100644 index 0000000..c4be105 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/README.md @@ -0,0 +1,27 @@ +# NightShift Imageboard Target + +This project was created with: + +```bash +nightshift init --template tutorial-imageboard +``` + +NightShift control files live in `.nightshift/`. Target application code should live under `src/`, tests under `tests/`, templates under `templates/`, and uploaded/generated static files under `static/`. + +Install target dependencies: + +```bash +python -m pip install flask pillow pytest +``` + +Validate the project: + +```bash +nightshift validate +``` + +Run the first task: + +```bash +nightshift run --task TASK-001 +``` diff --git a/nightshift/project_templates/tutorial-imageboard/nightshift.yaml b/nightshift/project_templates/tutorial-imageboard/nightshift.yaml new file mode 100644 index 0000000..98084b7 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/nightshift.yaml @@ -0,0 +1,98 @@ +project: + name: imageboard + root: . + task_file: .nightshift/tasks.md + artifact_dir: .nightshift + +safety: + require_clean_worktree: false + scoped_paths: + - src + - tests + - templates + - static + - schema.sql + - pyproject.toml + allowed_commands: + - python -m pytest -q + forbidden_commands: + - rm -rf + - git push + - curl | bash + +experiment: + label: imageboard-real-model + prompt_variant: ollama-qwen25-coder-14b-v1 + +agents: + planner: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.2 + system_prompt: .nightshift/agents/planner.md + + implementer: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/implementer.md + + reviewer: + backend: ollama + model: qwen2.5-coder:14b + temperature: 0.1 + system_prompt: .nightshift/agents/reviewer.md + +pipeline: + max_task_retries: 3 + continue_on_task_failure: false + stages: + - id: plan + type: agent + agent: planner + output: plan.md + + - id: context + type: repo_context + output: context-pack.md + + - id: implement + type: file_writer + agent: implementer + output: proposed.patch + + - id: normalize + type: patch_normalizer + output: normalized.patch + + - id: validate_patch + type: patch_validator + output: patch-validation.md + max_files: 10 + max_lines: 900 + on_fail: implement + + - id: apply_patch + type: patch_apply + mode: apply + output: patch-apply-output.txt + on_fail: implement + + - id: test + type: command + commands: + - python -m pytest -q + output: test-output.txt + shell: true + timeout_seconds: 20 + on_fail: implement + + - id: review + type: agent_review + agent: reviewer + on_fail: implement + output: review.md + + - id: summarize + type: summarize + output: final-notes.md diff --git a/nightshift/project_templates/tutorial-imageboard/src/imageboard/.gitkeep b/nightshift/project_templates/tutorial-imageboard/src/imageboard/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/src/imageboard/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/tutorial-imageboard/static/thumbs/.gitkeep b/nightshift/project_templates/tutorial-imageboard/static/thumbs/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/static/thumbs/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/tutorial-imageboard/static/uploads/.gitkeep b/nightshift/project_templates/tutorial-imageboard/static/uploads/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/static/uploads/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/tutorial-imageboard/templates/.gitkeep b/nightshift/project_templates/tutorial-imageboard/templates/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/templates/.gitkeep @@ -0,0 +1 @@ + diff --git a/nightshift/project_templates/tutorial-imageboard/tests/.gitkeep b/nightshift/project_templates/tutorial-imageboard/tests/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nightshift/project_templates/tutorial-imageboard/tests/.gitkeep @@ -0,0 +1 @@ + diff --git a/pyproject.toml b/pyproject.toml index 8d7f25c..e831abd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,3 +18,6 @@ nightshift = "nightshift.cli:main" [tool.setuptools.packages.find] include = ["nightshift*"] + +[tool.setuptools.package-data] +nightshift = ["project_templates/**/*"] diff --git a/tests/test_init.py b/tests/test_init.py index 8f93764..dc76dce 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -3,7 +3,7 @@ import tempfile import unittest from nightshift.errors import InitError -from nightshift.init import init_project +from nightshift.init import available_templates, init_project class InitProjectTests(unittest.TestCase): @@ -42,7 +42,7 @@ class InitProjectTests(unittest.TestCase): with tempfile.TemporaryDirectory() as directory: root = Path(directory) - written = init_project(root, template="imageboard") + written = init_project(root, template="tutorial-imageboard") self.assertIn(root / "nightshift.yaml", written) self.assertTrue((root / ".nightshift" / "tasks.md").exists()) @@ -54,6 +54,12 @@ class InitProjectTests(unittest.TestCase): (root / "nightshift.yaml").read_text(encoding="utf-8"), ) + def test_available_templates_includes_filesystem_templates(self) -> None: + self.assertIn("basic", available_templates()) + self.assertIn("real-long-running", available_templates()) + self.assertIn("real-simple", available_templates()) + self.assertIn("tutorial-imageboard", available_templates()) + def test_init_rejects_unknown_template(self) -> None: with tempfile.TemporaryDirectory() as directory: with self.assertRaisesRegex(InitError, "Unknown template"):