mirror of
https://github.com/khodges42/nightShift.git
synced 2026-06-14 10:08:37 +00:00
Add project templates and clean up a bit
This commit is contained in:
parent
5e5cd184b9
commit
b0f8d59707
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
11
nightshift/project_templates/basic/agents/implementer.md
Normal file
11
nightshift/project_templates/basic/agents/implementer.md
Normal file
|
|
@ -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.
|
||||
13
nightshift/project_templates/basic/agents/planner.md
Normal file
13
nightshift/project_templates/basic/agents/planner.md
Normal file
|
|
@ -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.
|
||||
12
nightshift/project_templates/basic/agents/reviewer.md
Normal file
12
nightshift/project_templates/basic/agents/reviewer.md
Normal file
|
|
@ -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: <short explanation>
|
||||
next_stage: <optional stage id>
|
||||
context_update: <compact useful note>
|
||||
67
nightshift/project_templates/basic/nightshift.yaml
Normal file
67
nightshift/project_templates/basic/nightshift.yaml
Normal file
|
|
@ -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
|
||||
10
nightshift/project_templates/basic/tasks.md
Normal file
10
nightshift/project_templates/basic/tasks.md
Normal file
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
<complete file content>
|
||||
```
|
||||
|
||||
Keep the implementation simple and testable.
|
||||
Include tests.
|
||||
Do not include explanations outside file blocks.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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: <short explanation>
|
||||
next_stage: <optional stage id>
|
||||
context_update: <compact useful note>
|
||||
|
||||
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.
|
||||
|
|
@ -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
|
||||
<complete file content>
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -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
|
||||
29
nightshift/project_templates/real-long-running/README.md
Normal file
29
nightshift/project_templates/real-long-running/README.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
151
nightshift/project_templates/real-long-running/nightshift.yaml
Normal file
151
nightshift/project_templates/real-long-running/nightshift.yaml
Normal file
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -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
|
||||
<complete file content>
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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: <short explanation>
|
||||
next_stage: <optional stage id>
|
||||
context_update: <compact useful note>
|
||||
|
||||
Use retry for fixable implementation or test failures.
|
||||
Use pass only when acceptance criteria are satisfied.
|
||||
|
|
@ -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
|
||||
14
nightshift/project_templates/real-simple/README.md
Normal file
14
nightshift/project_templates/real-simple/README.md
Normal file
|
|
@ -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/`.
|
||||
96
nightshift/project_templates/real-simple/nightshift.yaml
Normal file
96
nightshift/project_templates/real-simple/nightshift.yaml
Normal file
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
nightshift/project_templates/real-simple/tests/.gitkeep
Normal file
1
nightshift/project_templates/real-simple/tests/.gitkeep
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -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
|
||||
<complete file content>
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -16,4 +16,4 @@ After context is provided, write a short plan with:
|
|||
- tests to add or update
|
||||
- risks
|
||||
|
||||
Do not write code.
|
||||
Do not write code.
|
||||
|
|
@ -11,4 +11,4 @@ context_update: <compact useful note>
|
|||
|
||||
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.
|
||||
Use pass only when the acceptance criteria are satisfied.
|
||||
|
|
@ -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/<name>` and `/thread/<id>` 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/<name>` route showing threads for that board
|
||||
- Implements `/thread/<id>` 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
|
||||
27
nightshift/project_templates/tutorial-imageboard/README.md
Normal file
27
nightshift/project_templates/tutorial-imageboard/README.md
Normal file
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -18,3 +18,6 @@ nightshift = "nightshift.cli:main"
|
|||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["nightshift*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
nightshift = ["project_templates/**/*"]
|
||||
|
|
|
|||
|
|
@ -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"):
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user