9 Commits

Author SHA1 Message Date
e766210920 feat: introduce AI agent framework and task tooling
- add AGENT.md describing the agent system
- add AGENT_WORKFLOW.md defining the execution workflow
- add AI_TASK_TEMPLATE.md for structured task prompts
- add scripts/ai-task CLI tool for generating standardized agent tasks
2026-03-16 01:38:36 +01:00
6849631cc1 docs: finalize AI logging format system
- align ai-worklog and ai-prompts with the current logging framework
- require timestamped structured log entries
- finalize logging rules in instructions-agent.md
2026-03-16 01:33:02 +01:00
7c87d6f1f0 docs: require timestamp in AI log entries
- enforce YYYY-MM-DD HH:MM format in AI logs
- update AI BUILD ENTRY and AI PLAN ENTRY examples
- improve chronological accuracy of worklog
2026-03-16 00:32:57 +01:00
60723c33bc feat: improve terminal navigation and UI behavior
- refine tree and history navigation behavior
- improve scrolling continuity in the terminal UI
- keep AI worklog and prompt log updated
- validate changes with project quality checks
2026-03-16 00:28:27 +01:00
6195f7b8a3 docs: introduce standard AI log entry format
- add standardized AI BUILD ENTRY and AI PLAN ENTRY format
- improve consistency of AI development logs
- update instructions-agent.md
2026-03-15 22:22:35 +01:00
801544951f docs: introduce mandatory AI logging system
- add AI worklog and prompt logs
- enforce logging rules in instructions-agent.md
- require AI agents to record plan and build tasks
- establish traceable AI-assisted development workflow
2026-03-15 18:26:22 +01:00
65508d263f feat: implement history mode and AI development logging
- add dedicated history mode UI (toggle with `h`)
- allow navigation inside history with arrows
- allow selecting history entries with Enter
- add tests for history mode behavior
- introduce AI development logs (`ai-worklog.md`, `ai-prompts.md`)
- add AI worklog policy to `instructions-agent.md`
2026-03-15 17:46:11 +01:00
2bdfbe25d5 debug: add temporary history overlay 2026-03-15 12:03:11 +01:00
12d2577a31 feat: improve navigation behaviour 2026-03-14 18:54:20 +01:00
9 changed files with 1676 additions and 28 deletions

260
AGENT.md Normal file
View File

@@ -0,0 +1,260 @@
# AGENT.md
AI Agent Development Guide
This file defines the operational rules that AI agents must follow when working in this repository.
The goal is to ensure:
- reproducible AI-assisted development
- traceable decisions
- consistent repository history
- safe automated contributions
This document complements:
- `instructions-agent.md`
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
---
# Core Principles
Agents must prioritize:
1. Deterministic changes
2. Minimal scope modifications
3. Explicit logging of work
4. Repository stability
Agents must never:
- fabricate repository state
- invent files that do not exist
- modify unrelated parts of the project
- skip validation steps
---
# Development Workflow
Agents operate using two primary task types:
## PLAN
Used to:
- investigate problems
- inspect repository state
- propose architecture changes
PLAN tasks must:
- analyze the current implementation
- identify the root cause
- propose minimal changes
PLAN tasks **must not modify code**.
---
## BUILD
Used to:
- implement changes
- update code
- add tests
- update documentation
BUILD tasks may:
- modify source files
- update tests
- update documentation
BUILD tasks must:
- remain minimal in scope
- preserve existing behavior unless explicitly changed
---
# Validation Requirements
All BUILD tasks must run:
```
make fix
make quality
```
These commands must pass before changes are considered valid.
---
# Logging System
All significant AI actions must be recorded.
Two log files are used:
```
docs/ai-worklog.md
docs/ai-prompts.md
```
---
# Worklog Entries
Executed work must be recorded in:
```
docs/ai-worklog.md
```
Each entry must follow the standard format defined in `instructions-agent.md`.
Mandatory fields:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- Objective
- Scope
- Files Modified / Files Inspected
- Key Decisions / Proposed Changes
- Validation
- Result
- Open Issues
---
# Prompt Log
Prompts that trigger significant work must be recorded in:
```
docs/ai-prompts.md
```
Each prompt entry must include:
- Date
- Task ID
- Agent
- Task
- Scope
- Prompt Summary
- Result Summary
Chain-of-thought reasoning must **never be stored**.
---
# Task IDs
All tasks must include a unique identifier.
Format:
```
BUILD-YYYYMMDD-XXX
PLAN-YYYYMMDD-XXX
```
Examples:
```
BUILD-20260315-001
PLAN-20260316-002
```
This allows prompts and worklog entries to be correlated.
---
# Git Rules
Agents must follow these repository rules.
Agents must **not commit or push** unless explicitly requested.
When committing:
Use structured commit messages.
Example:
```
feat: improve terminal navigation behavior
- refine tree navigation
- improve scrolling continuity
- update AI logs
```
Documentation-only updates should use:
```
docs:
```
---
# Code Modification Rules
Agents should prefer:
- small targeted patches
- isolated logic changes
- preserving existing abstractions
Agents should avoid:
- rewriting entire modules
- introducing unnecessary dependencies
- altering unrelated code paths
---
# UI Changes
When modifying UI behavior:
- keep navigation predictable
- preserve keyboard ergonomics
- ensure selections remain visible
- maintain compatibility with history mode
---
# Testing Rules
Whenever logic changes:
- update existing tests
- add focused tests when appropriate
Tests should validate behavior, not implementation details.
---
# Scope Discipline
Agents must strictly respect scope defined in prompts.
If additional changes appear necessary:
Agents must propose them in a PLAN task before implementing.
---
# Repository Safety
Agents must not:
- delete important files
- alter project configuration without justification
- introduce breaking changes without explicit instruction
---
# End of Document
---

249
AGENT_WORKFLOW.md Normal file
View File

@@ -0,0 +1,249 @@
# AGENT_WORKFLOW.md
AI Agent Operational Workflow
This document defines the deterministic workflow that AI agents must follow when performing work in this repository.
The workflow ensures:
- predictable development cycles
- traceable decisions
- minimal risk of unintended changes
- consistent logging of AI actions
This file complements:
- `AGENT.md`
- `instructions-agent.md`
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
---
# Standard Workflow
Every AI task must follow this sequence:
PLAN → REVIEW → BUILD → VALIDATE → LOG
This sequence must not be skipped.
---
# 1. PLAN
Goal:
Understand the problem and determine the minimal solution.
Actions:
- inspect repository state
- read relevant source files
- analyze existing architecture
- identify root cause of the problem
- propose a minimal change
Rules:
- PLAN must **not modify code**
- PLAN must **not change files**
- PLAN must only produce analysis and a proposal
Output should include:
- diagnosis summary
- proposed changes
- expected file scope
---
# 2. REVIEW
Goal:
Validate the plan before implementing it.
Actions:
- confirm the proposed scope is minimal
- ensure no unrelated components are affected
- verify the change aligns with project architecture
Rules:
- If the scope expands unexpectedly, return to PLAN.
---
# 3. BUILD
Goal:
Implement the approved change.
Actions:
- modify only the files defined in scope
- preserve existing architecture
- avoid unnecessary refactors
- maintain compatibility with existing behavior
BUILD tasks may modify:
- source files
- tests
- documentation
BUILD tasks must remain minimal and targeted.
---
# 4. VALIDATE
Goal:
Ensure the repository remains stable.
Agents must run:
```
make fix
make quality
```
Validation must pass before changes are considered complete.
---
# 5. LOG
Goal:
Record the AI activity for traceability.
Two log files must be updated.
### Work Log
```
docs/ai-worklog.md
```
Record:
- AI BUILD ENTRY
- or AI PLAN ENTRY
Including:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- Objective
- Scope
- Files Modified / Files Inspected
- Key Decisions / Proposed Changes
- Validation
- Result
- Open Issues
### Prompt Log
```
docs/ai-prompts.md
```
Record:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task
- prompt summary
- scope
- result summary
Do not store chain-of-thought reasoning.
---
# Task ID Coordination
Every PLAN or BUILD must generate a unique Task ID.
Format:
```
PLAN-YYYYMMDD-XXX
BUILD-YYYYMMDD-XXX
```
Example:
```
PLAN-20260316-001
BUILD-20260316-002
```
The same Task ID must appear in:
- ai-prompts.md
- ai-worklog.md
---
# Commit Workflow
Agents must **not commit or push automatically** unless explicitly instructed.
When commits are requested:
1. Validate repository state
2. Stage modified files
3. Use structured commit messages
Example:
```
feat: improve terminal navigation
- refine tree navigation
- improve scrolling
- update AI logs
```
Documentation-only updates should use:
```
docs:
```
---
# Failure Handling
If validation fails:
Agents must:
1. stop the BUILD process
2. report the error
3. propose a fix
Agents must **not silently bypass failing checks**.
---
# Scope Control
Agents must strictly respect the defined scope.
If additional changes appear necessary:
- return to PLAN
- propose a new change
Do not expand scope during BUILD.
---
# End of Document

145
AI_TASK_TEMPLATE.md Normal file
View File

@@ -0,0 +1,145 @@
# AI Task Template
Reusable prompt template for launching tasks with AI agents.
This template enforces the repository workflow defined in:
- AGENT.md
- AGENT_WORKFLOW.md
- instructions-agent.md
It ensures every task follows the deterministic sequence:
PLAN → REVIEW → BUILD → VALIDATE → LOG
---
# Basic Task Structure
Use the following structure when requesting work from an AI agent.
```
Task Type:
PLAN | BUILD
Task ID:
PLAN-YYYYMMDD-XXX | BUILD-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Short description of the goal.
Context:
Relevant background about the current repository state.
Scope:
List of files or directories the agent may modify or inspect.
Requirements:
Detailed description of the behavior that must be implemented or analyzed.
Constraints:
Things the agent must NOT change or must preserve.
Testing:
Describe expected tests or validations.
Validation:
The agent must run:
make fix
make quality
Logging requirements:
- Append an entry to docs/ai-worklog.md
- Append an entry to docs/ai-prompts.md
- Include Date, Task ID, Agent, Scope, Result
Git requirements:
- Do NOT commit or push unless explicitly requested.
Expected Report:
The agent must summarize:
- files changed
- behavior changes
- validation results
```
---
# Example BUILD Task
```
Task Type:
BUILD
Task ID:
BUILD-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Improve vertical scrolling behavior in the terminal UI.
Scope:
- src/app/ui.py
- tests/test_ui.py
Requirements:
Ensure the selected entry remains visible when navigating through long directory lists.
Constraints:
Do not modify navigator logic.
Testing:
Add helper tests if necessary.
Validation:
make fix
make quality
Logging requirements:
Update ai-worklog.md and ai-prompts.md using the standard log format.
```
---
# Example PLAN Task
```
Task Type:
PLAN
Task ID:
PLAN-YYYYMMDD-XXX
Date:
YYYY-MM-DD HH:MM
Objective:
Investigate inconsistent navigation behavior.
Scope:
- src/app/ui.py
- src/app/navigator.py
Requirements:
Analyze the current navigation flow and propose a minimal fix.
Constraints:
Do not modify code.
Expected Output:
- diagnosis summary
- proposed changes
- expected file scope
```
---
# Notes
This template keeps AI interactions consistent across tasks and helps maintain a clean development history.

155
docs/ai-prompts.md Normal file
View File

@@ -0,0 +1,155 @@
# AI Prompts Log
This file stores the prompts that triggered significant AI-driven development tasks.
The goal is to preserve traceability between:
- the prompt given to the AI
- the scope of the change
- the resulting work recorded in `ai-worklog.md`
Each entry must include:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task type (plan/build)
- Scope
- Prompt summary
- Result summary
Only record prompts that:
- change architecture
- introduce new behavior
- modify important logic
- affect project structure
- debug complex issues
Do not store chain-of-thought reasoning. Only store the prompt intent and a concise result summary.
---
### BUILD PROMPT ENTRY
Date: 2026-03-15 18:05
Task ID: BUILD-20260315-001
Agent: OpenCode
Task: build
Scope:
- src/app/ui.py
- tests/test_ui.py
Prompt Summary:
Implement a dedicated history mode in `src/app/ui.py` so pressing `h` hides the tree, shows only history entries, supports Up/Down navigation, and uses Enter to select a history entry and return to the normal tree view.
Result Summary:
Added a dedicated history mode with its own selection state, removed the temporary overlay behavior, preserved existing `b`/`f` and normal tree navigation behavior, and added focused UI tests.
---
### BUILD PROMPT ENTRY
Date: 2026-03-15 19:25
Task ID: BUILD-20260315-002
Agent: OpenCode
Task: build
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Prompt Summary:
Introduce a mandatory AI logging system by updating `instructions-agent.md` and adding repository logs for AI work entries and prompt summaries.
Result Summary:
Added enforceable AI logging rules to `instructions-agent.md`, created `docs/ai-worklog.md` and `docs/ai-prompts.md`, and established a traceable workflow for recording significant AI-assisted tasks.
---
### BUILD PROMPT ENTRY
Date: 2026-03-15 20:00
Task ID: BUILD-20260315-003
Agent: OpenCode
Task: build
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Prompt Summary:
Introduce a standard AI log entry format in `instructions-agent.md` and record the change in the repository AI logs.
Result Summary:
Added standardized `AI BUILD ENTRY` and `AI PLAN ENTRY` formats, improving consistency and long-term traceability of AI-assisted development.
---
### BUILD PROMPT ENTRY
Date: 2026-03-15 21:35
Task ID: BUILD-20260315-004
Agent: OpenCode
Task: build
Scope:
- src/app/ui.py
- tests/test_ui.py
Prompt Summary:
Improve keyboard navigation in `src/app/ui.py` so Right expands or enters, Left collapses or goes to the parent, Enter confirms the selected destination, and ESC cancels back to the starting directory.
Result Summary:
Updated the tree-mode key handling in the terminal UI, preserved history mode behavior, and added focused UI tests for the new navigation rules.
---
### BUILD PROMPT ENTRY
Date: 2026-03-15 22:20
Task ID: BUILD-20260315-005
Agent: OpenCode
Task: build
Scope:
- src/app/ui.py
- tests/test_ui.py
Prompt Summary:
Improve vertical scrolling in `src/app/ui.py` so the selected entry always remains visible in tree mode and history mode when the list is taller than the screen.
Result Summary:
Added UI scroll offset handling for tree and history views, kept existing navigation behavior unchanged, and added focused tests for the scroll helper logic.
---
### BUILD PROMPT ENTRY
Date: 2026-03-16 00:27
Task ID: BUILD-20260316-001
Agent: OpenCode
Task: build
Scope:
- src/app/ui.py
- tests/test_ui.py
Prompt Summary:
Refine terminal UI navigation, maintain dedicated history mode, and ensure scrolling continuity so selections remain visible.
Result Summary:
Updated terminal UI behavior, consolidated navigation improvements, and ensured consistent scrolling and selection visibility across tree and history modes.
---
### BUILD PROMPT ENTRY
Date: 2026-03-16 01:26
Task ID: BUILD-20260316-002
Agent: OpenCode
Task: build
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Prompt Summary:
Finalize the AI logging format system by requiring timestamped structured log entries and aligning the repository worklog and prompt log with the current logging framework.
Result Summary:
Updated the logging instructions, normalized the prompt log to the structured entry format, and added matching repository log entries for the finalized AI logging system.

254
docs/ai-worklog.md Normal file
View File

@@ -0,0 +1,254 @@
# AI Worklog
This file records important AIassisted development actions.
The log is designed for longterm traceability of AIdriven development.
Each entry must include:
- Date (YYYY-MM-DD HH:MM)
- Task ID
- Agent
- Task type (plan/build)
- Objective
- Scope
- Files Modified / Files Inspected
- Key Decisions or Proposed Changes
- Validation
- Result
- Open Issues
Entries must remain chronological.
---
### AI BUILD ENTRY
Date: 2026-03-15 18:10
Task ID: BUILD-20260315-001
Agent: OpenCode
Task: build
Objective: Replace the temporary history debug overlay with a dedicated history mode in the terminal UI.
Scope:
- src/app/ui.py
- tests/test_ui.py
Files Modified:
- src/app/ui.py
- tests/test_ui.py
Key Decisions:
- Keep `b` and `f` unchanged.
- Add separate history-mode selection state inside the UI.
- Hide the tree while history mode is active.
- Use Enter to select a history entry and return to the tree view.
Validation:
- make fix
- make quality
Result:
- History mode now shows only the history list and allows navigation and selection of past directories.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-15 19:30
Task ID: BUILD-20260315-002
Agent: OpenCode
Task: build
Objective: Introduce mandatory AI logging rules and repository logging workflow.
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Files Modified:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Key Decisions:
- Require AI agents to log plan and build tasks.
- Separate logs for work results and prompts.
- Keep entries concise and userfocused.
Validation:
- not run
Result:
- Repository now enforces AI logging via instructions-agent.md.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-15 20:05
Task ID: BUILD-20260315-003
Agent: OpenCode
Task: build
Objective: Introduce the standard AI log entry format and improve logging consistency.
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Files Modified:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Key Decisions:
- Define AI BUILD ENTRY and AI PLAN ENTRY blocks.
- Standardize structure for longterm repository traceability.
Validation:
- make fix (passed)
- make quality (passed)
Result:
- Repository now uses a standardized AI logging format.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-15 21:40
Task ID: BUILD-20260315-004
Agent: OpenCode
Task: build
Objective: Improve keyboard navigation behavior in the terminal UI for tree mode.
Scope:
- src/app/ui.py
- tests/test_ui.py
Files Modified:
- src/app/ui.py
- tests/test_ui.py
Key Decisions:
- Right expands collapsed directories and enters expanded directories.
- Left collapses expanded directories or navigates to the parent directory.
- Enter confirms the selected path.
- ESC cancels and returns to the original directory.
Validation:
- make fix (passed)
- make quality (passed)
Result:
- Tree navigation semantics improved with clearer expand, enter, and cancel behavior.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-15 22:25
Task ID: BUILD-20260315-005
Agent: OpenCode
Task: build
Objective: Implement vertical scrolling so the selected entry always remains visible.
Scope:
- src/app/ui.py
- tests/test_ui.py
Files Modified:
- src/app/ui.py
- tests/test_ui.py
Key Decisions:
- Introduce scroll offsets in the UI state.
- Clamp offsets to keep the selected row visible.
- Apply the same mechanism to history mode.
Validation:
- make fix (passed)
- make quality (passed)
Result:
- Tree and history views now scroll when navigating beyond the visible screen area.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-16 00:27
Task ID: BUILD-20260316-001
Agent: OpenCode
Task: build
Objective: Consolidate terminal UI navigation improvements and document updated behavior.
Scope:
- src/app/ui.py
- tests/test_ui.py
- docs/ai-worklog.md
- docs/ai-prompts.md
Files Modified:
- src/app/ui.py
- tests/test_ui.py
- docs/ai-worklog.md
- docs/ai-prompts.md
Key Decisions:
- Maintain dedicated history mode.
- Refine tree-mode keyboard behavior.
- Keep UI logic isolated from navigator logic.
Validation:
- make fix (passed)
- make quality (passed)
Result:
- Terminal UI now includes history mode, improved navigation semantics, and reliable vertical scrolling.
Open Issues:
- None
---
### AI BUILD ENTRY
Date: 2026-03-16 01:26
Task ID: BUILD-20260316-002
Agent: OpenCode
Task: build
Objective: Finalize the AI logging format system and align repository log files with the structured logging framework.
Scope:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Files Modified:
- instructions-agent.md
- docs/ai-worklog.md
- docs/ai-prompts.md
Key Decisions:
- Require timestamped log entries with date and time, Task ID, Agent, and Scope fields.
- Keep the worklog and prompt log aligned with the current structured logging framework.
- Normalize prompt logging into a single consistent `BUILD PROMPT ENTRY` format.
Validation:
- make fix (passed)
- make quality (passed)
Result:
- The AI logging system now uses consistent structured entries across repository instructions, worklog history, and prompt history.
Open Issues:
- None

View File

@@ -59,3 +59,149 @@ Agent rules:
3. If the specification conflicts with a previous assumption, the specification takes precedence.
4. Keep implementation aligned with the MVP scope unless explicitly asked to extend it.
5. After code changes, run the appropriate validation workflow defined by this repository.
## AI Worklog Policy
This repository maintains an AI-assisted development log.
Files:
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
Rules:
1. After every significant `plan` or `build` task, append a concise worklog entry to `docs/ai-worklog.md`.
2. Each worklog entry must include:
- date
- task type (`plan` or `build`)
- short objective
- files inspected or modified
- key decisions
- validation commands run
- result
- unresolved issues if any
3. When a prompt meaningfully changes architecture, behavior, workflow, debugging direction, or project structure, append the prompt (or a concise cleaned version of it) to `docs/ai-prompts.md`.
4. Do not store private chain-of-thought or internal reasoning.
5. Store only concise, user-facing summaries of what was done.
6. Keep entries chronological and easy to scan.
## Mandatory AI Logging
Every significant AI interaction in this repository must be logged.
This applies to:
- `plan` tasks
- `build` tasks
- debugging tasks
- documentation tasks
- architectural discussions that affect the project
Required files:
- `docs/ai-worklog.md`
- `docs/ai-prompts.md`
Mandatory rules:
1. After every significant `plan` task, append a concise entry to `docs/ai-worklog.md`.
2. After every significant `build` task, append a concise entry to `docs/ai-worklog.md`.
3. If the task was driven by a meaningful prompt, also append an entry to `docs/ai-prompts.md`.
4. Logging is part of the task itself and must not be skipped.
5. The task is not complete until the logs are updated.
Each worklog entry must include:
- date
- task type (`plan` or `build`)
- short objective
- files inspected or modified
- key decisions
- validation commands run, if any
- result
- unresolved issues, if any
Validation logging rule:
- If validation commands such as `make fix`, `make quality`, `pytest`, or other checks are executed during the task, the worklog entry must record them explicitly.
- The value `validation: not run` may only be used if no validation commands were executed.
- The worklog entry must reflect the actual commands run during the task.
Each prompt log entry must include:
- date
- task type (`plan` or `build`)
- short prompt summary
- scope
- result summary
Rules:
- Do not store chain-of-thought or private reasoning.
- Store only concise user-facing summaries.
- Keep entries chronological.
- Even small but meaningful tasks must be logged.
## Standard AI Log Entry Format
To ensure consistency and readability of long AI-assisted development histories, all log entries must follow a standardized block format.
Agents must write log entries using the following structure.
Example for build tasks:
### AI BUILD ENTRY
Date: YYYY-MM-DD HH:MM
Task ID: BUILD-YYYYMMDD-XXX
Agent: OpenCode
Task: build
Objective: short description of the task
Scope:
- src/app/module.py
- tests/test_module.py
Files Modified:
- file/path/example.py
- another/file.md
Key Decisions:
- short bullet explaining important choices
Validation:
- make fix (passed)
- make quality (passed)
Result:
- short description of what changed or was achieved
Open Issues:
- optional list of unresolved problems
Example for plan tasks:
### AI PLAN ENTRY
Date: YYYY-MM-DD HH:MM
Task ID: PLAN-YYYYMMDD-XXX
Agent: OpenCode
Task: plan
Objective: short description of planning objective
Scope:
- src/app/module.py
- specs/module_spec.md
Files Inspected:
- src/app/example.py
- specs/example_spec.md
Proposed Changes:
- summary of planned changes
Notes:
- optional relevant observations
Rules:
- Always use these headers exactly (`AI BUILD ENTRY` or `AI PLAN ENTRY`).
- Keep entries concise and structured.
- Do not include chain-of-thought reasoning.
- Use bullet points when possible.
- This format must be used when updating `docs/ai-worklog.md`.
- The Date field must include both date and time using the format YYYY-MM-DD HH:MM.
- Every log entry must include a `Task ID`.
- `Task ID` format must be `BUILD-YYYYMMDD-XXX` or `PLAN-YYYYMMDD-XXX`.
- `Agent` must identify the tool or AI system performing the task (e.g., OpenCode).
- `Scope` must describe the main files or project areas affected.
- These fields make the AI worklog suitable for long-term traceability and large histories.

112
scripts/ai-task Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# ai-task
# Helper CLI to generate AI task prompts based on AI_TASK_TEMPLATE.md
# Usage:
# scripts/ai-task build "Objective text" [scope...]
# scripts/ai-task plan "Objective text" [scope...]
set -e
TYPE="$1"
OBJECTIVE="$2"
shift 2 || true
SCOPE=("$@")
if [[ -z "$TYPE" || -z "$OBJECTIVE" ]]; then
echo "Usage: ai-task <plan|build> \"Objective\" [scope files...]"
exit 1
fi
TYPE_LOWER=$(echo "$TYPE" | tr '[:upper:]' '[:lower:]')
TYPE_UPPER=$(echo "$TYPE_LOWER" | tr '[:lower:]' '[:upper:]')
DATE=$(date "+%Y-%m-%d %H:%M")
DAY=$(date "+%Y%m%d")
case "$TYPE_LOWER" in
build)
PREFIX="BUILD"
;;
plan)
PREFIX="PLAN"
;;
*)
echo "Error: task type must be 'plan' or 'build'"
exit 1
;;
esac
WORKLOG_FILE="docs/ai-worklog.md"
LAST_SEQ=0
if [[ -f "$WORKLOG_FILE" ]]; then
LAST_MATCH=$(grep -Eo "${PREFIX}-${DAY}-[0-9]{3}" "$WORKLOG_FILE" | tail -n 1 || true)
if [[ -n "$LAST_MATCH" ]]; then
LAST_SEQ=${LAST_MATCH##*-}
LAST_SEQ=$((10#$LAST_SEQ))
fi
fi
NEXT_SEQ=$(printf "%03d" $((LAST_SEQ + 1)))
TASK_ID="${PREFIX}-${DAY}-${NEXT_SEQ}"
# Build scope block
cat <<EOF
Task Type:
${TYPE_UPPER}
Task ID:
${TASK_ID}
Date:
${DATE}
Objective:
${OBJECTIVE}
Context:
<describe repository state if needed>
Scope:
EOF
if [ ${#SCOPE[@]} -gt 0 ]; then
for item in "${SCOPE[@]}"; do
printf '%s\n' "- ${item}"
done
else
printf '%s\n' "- <fill scope>"
fi
cat <<EOF
Requirements:
<describe expected behavior>
Constraints:
- follow AGENT_WORKFLOW.md
- keep scope minimal
- preserve existing behavior unless explicitly changed
Testing:
<describe expected tests>
Validation:
make fix
make quality
Logging requirements:
- append entry to docs/ai-worklog.md
- append entry to docs/ai-prompts.md
- include Date, Task ID, Agent, Scope
- use Task ID: ${TASK_ID}
Git requirements:
- do NOT commit or push unless explicitly requested
Expected Report:
- files changed
- behavior changes
- validation results
EOF

View File

@@ -22,6 +22,14 @@ class RenderLine:
is_selected: bool
@dataclass(frozen=True, slots=True)
class HistoryLine:
"""A rendered history row ready for terminal output."""
text: str
is_selected: bool
def move_selection(current_index: int, step: int, entry_count: int) -> int:
"""Move the selected index within the available entry range."""
@@ -57,9 +65,46 @@ def build_render_lines(navigator: Navigator) -> list[RenderLine]:
]
def build_history_lines(
entries: tuple[Path, ...], selected_index: int
) -> list[HistoryLine]:
"""Build all visible lines for history mode."""
return [
HistoryLine(text=str(path), is_selected=index == selected_index)
for index, path in enumerate(entries)
]
def clamp_scroll_offset(
selected_index: int, scroll_offset: int, visible_count: int, total_count: int
) -> int:
"""Keep the selected row within the visible scrolling window."""
if visible_count <= 0 or total_count <= visible_count:
return 0
max_offset = total_count - visible_count
offset = max(0, min(scroll_offset, max_offset))
if selected_index < offset:
return selected_index
if selected_index >= offset + visible_count:
return selected_index - visible_count + 1
return offset
class TerminalUI:
"""Minimal curses UI for the directory navigator MVP."""
def __init__(self) -> None:
self._history_mode = False
self._history_selected_index = 0
self._tree_scroll_offset = 0
self._history_scroll_offset = 0
def run(self, navigator: Navigator) -> Path:
"""Start the interactive session and return the final directory path."""
@@ -70,14 +115,122 @@ class TerminalUI:
def _run_session(self, stdscr: curses.window, navigator: Navigator) -> Path:
curses.curs_set(0)
stdscr.keypad(True)
original_path = navigator.current_path
while True:
self._render(stdscr, navigator)
key = stdscr.getch()
if key == ESCAPE_KEY:
return navigator.current_path
return original_path
if key == ord("h"):
self._toggle_history_mode(navigator)
continue
if self._history_mode:
self._handle_history_key(key, navigator)
continue
tree_result = self._handle_tree_key(key, navigator)
if tree_result is not None:
return tree_result
def _render(self, stdscr: curses.window, navigator: Navigator) -> None:
stdscr.erase()
height, width = stdscr.getmaxyx()
if self._history_mode:
self._render_history_mode(stdscr, navigator, width=width, height=height)
stdscr.refresh()
return
path_text = str(navigator.current_path)
stdscr.addnstr(0, 0, path_text, width - 1)
lines = build_render_lines(navigator)
visible_count = max(0, height - 2)
self._tree_scroll_offset = clamp_scroll_offset(
navigator.selected_index,
self._tree_scroll_offset,
visible_count,
len(lines),
)
for row, line in enumerate(
lines[self._tree_scroll_offset : self._tree_scroll_offset + visible_count],
start=2,
):
if row >= height:
break
attributes = curses.A_REVERSE if line.is_selected else curses.A_NORMAL
stdscr.addnstr(row, 0, line.text, width - 1, attributes)
stdscr.refresh()
def _render_history_mode(
self, stdscr: curses.window, navigator: Navigator, width: int, height: int
) -> None:
history_lines = build_history_lines(
navigator.history.entries(), self._history_selected_index
)
start_row = 1
visible_count = max(0, height - start_row - 1)
self._history_scroll_offset = clamp_scroll_offset(
self._history_selected_index,
self._history_scroll_offset,
visible_count,
len(history_lines),
)
stdscr.addnstr(start_row, 0, "History:", width - 1, curses.A_BOLD)
for index, line in enumerate(
history_lines[
self._history_scroll_offset : self._history_scroll_offset
+ visible_count
],
start=1,
):
row = start_row + index
if row >= height:
break
attributes = curses.A_REVERSE if line.is_selected else curses.A_NORMAL
stdscr.addnstr(row, 0, line.text, width - 1, attributes)
def _toggle_history_mode(self, navigator: Navigator) -> None:
self._history_mode = not self._history_mode
if self._history_mode:
self._history_selected_index = self._get_current_history_index(navigator)
self._history_scroll_offset = 0
def _handle_history_key(self, key: int, navigator: Navigator) -> None:
history_entries = navigator.history.entries()
if key == curses.KEY_UP:
self._history_selected_index = move_selection(
self._history_selected_index,
step=-1,
entry_count=len(history_entries),
)
return
if key == curses.KEY_DOWN:
self._history_selected_index = move_selection(
self._history_selected_index,
step=1,
entry_count=len(history_entries),
)
return
if key in (curses.KEY_ENTER, 10, 13) and history_entries:
navigator.select_history_entry(self._history_selected_index)
self._history_mode = False
self._tree_scroll_offset = 0
def _handle_tree_key(self, key: int, navigator: Navigator) -> Path | None:
if key == curses.KEY_UP:
navigator.set_selected_index(
move_selection(
@@ -86,7 +239,7 @@ class TerminalUI:
entry_count=len(navigator.visible_entries),
)
)
continue
return None
if key == curses.KEY_DOWN:
navigator.set_selected_index(
@@ -96,34 +249,52 @@ class TerminalUI:
entry_count=len(navigator.visible_entries),
)
)
continue
return None
if key == curses.KEY_RIGHT:
navigator.expand_selected_directory()
continue
was_expanded = navigator.selected_entry.is_expanded
if not navigator.expand_selected_directory():
navigator.enter_selected_directory()
self._tree_scroll_offset = 0
return None
if was_expanded:
navigator.enter_selected_directory()
self._tree_scroll_offset = 0
return None
if key == curses.KEY_LEFT:
navigator.collapse_selected_directory()
continue
if not navigator.collapse_selected_directory():
navigator.go_to_parent_directory()
self._tree_scroll_offset = 0
return None
if key == ord("b"):
navigator.go_back()
self._tree_scroll_offset = 0
return None
if key == ord("f"):
navigator.go_forward()
self._tree_scroll_offset = 0
return None
if key in (curses.KEY_ENTER, 10, 13):
navigator.enter_selected_directory()
return navigator.selected_entry.path
def _render(self, stdscr: curses.window, navigator: Navigator) -> None:
stdscr.erase()
height, width = stdscr.getmaxyx()
return None
path_text = str(navigator.current_path)
stdscr.addnstr(0, 0, path_text, width - 1)
def _get_current_history_index(self, navigator: Navigator) -> int:
history_entries = navigator.history.entries()
for row, line in enumerate(build_render_lines(navigator), start=2):
if row >= height:
break
for index in range(len(history_entries) - 1, -1, -1):
if history_entries[index] == navigator.current_path:
return index
attributes = curses.A_REVERSE if line.is_selected else curses.A_NORMAL
stdscr.addnstr(row, 0, line.text, width - 1, attributes)
stdscr.refresh()
return 0
@contextmanager

View File

@@ -1,11 +1,16 @@
import curses
from pathlib import Path
import pytest
from app.navigator import Navigator, VisibleEntry
from app.ui import (
HistoryLine,
RenderLine,
TerminalUI,
build_history_lines,
build_render_lines,
clamp_scroll_offset,
format_entry,
move_selection,
open_terminal_streams,
@@ -65,6 +70,157 @@ def test_build_render_lines_marks_selected_entry(tmp_path: Path) -> None:
]
def test_build_history_lines_marks_selected_history_entry(tmp_path: Path) -> None:
first = tmp_path / "first"
second = tmp_path / "second"
assert build_history_lines((first, second), selected_index=1) == [
HistoryLine(text=str(first), is_selected=False),
HistoryLine(text=str(second), is_selected=True),
]
def test_clamp_scroll_offset_keeps_selection_visible_when_moving_down() -> None:
assert clamp_scroll_offset(5, scroll_offset=0, visible_count=3, total_count=10) == 3
def test_clamp_scroll_offset_keeps_selection_visible_when_moving_up() -> None:
assert clamp_scroll_offset(1, scroll_offset=4, visible_count=3, total_count=10) == 1
def test_clamp_scroll_offset_returns_zero_when_all_items_fit() -> None:
assert clamp_scroll_offset(2, scroll_offset=1, visible_count=5, total_count=3) == 0
def test_toggle_history_mode_selects_current_history_entry(tmp_path: Path) -> None:
root = tmp_path / "root"
child = root / "child"
root.mkdir()
child.mkdir()
navigator = Navigator(root)
navigator.set_selected_index(1)
navigator.enter_selected_directory()
ui = TerminalUI()
ui._toggle_history_mode(navigator)
assert ui._history_mode is True
assert ui._history_selected_index == 1
def test_history_mode_enter_selects_entry_and_exits(tmp_path: Path) -> None:
root = tmp_path / "root"
first = root / "first"
second = root / "second"
root.mkdir()
first.mkdir()
second.mkdir()
navigator = Navigator(root)
navigator.set_selected_index(1)
navigator.enter_selected_directory()
navigator.go_to_parent_directory()
navigator.set_selected_index(2)
navigator.enter_selected_directory()
ui = TerminalUI()
ui._toggle_history_mode(navigator)
ui._handle_history_key(curses.KEY_UP, navigator)
ui._handle_history_key(10, navigator)
assert ui._history_mode is False
assert navigator.current_path == root.resolve()
assert navigator.selected_index == 0
def test_history_mode_toggle_off_keeps_current_directory(tmp_path: Path) -> None:
root = tmp_path / "root"
root.mkdir()
navigator = Navigator(root)
ui = TerminalUI()
ui._toggle_history_mode(navigator)
ui._toggle_history_mode(navigator)
assert ui._history_mode is False
assert navigator.current_path == root.resolve()
def test_right_arrow_expands_collapsed_directory(tmp_path: Path) -> None:
workspace = tmp_path / "workspace"
scripts = workspace / "scripts"
build = scripts / "build"
workspace.mkdir()
scripts.mkdir()
build.mkdir()
navigator = Navigator(workspace)
navigator.set_selected_index(1)
ui = TerminalUI()
assert ui._handle_tree_key(curses.KEY_RIGHT, navigator) is None
assert navigator.current_path == workspace.resolve()
assert [entry.name for entry in navigator.visible_entries] == [
"..",
"scripts",
"build",
]
def test_right_arrow_enters_already_expanded_directory(tmp_path: Path) -> None:
workspace = tmp_path / "workspace"
scripts = workspace / "scripts"
build = scripts / "build"
workspace.mkdir()
scripts.mkdir()
build.mkdir()
navigator = Navigator(workspace)
navigator.set_selected_index(1)
navigator.expand_selected_directory()
ui = TerminalUI()
assert ui._handle_tree_key(curses.KEY_RIGHT, navigator) is None
assert navigator.current_path == scripts.resolve()
assert navigator.selected_index == 0
def test_left_arrow_goes_to_parent_when_selected_entry_is_collapsed(
tmp_path: Path,
) -> None:
parent = tmp_path / "parent"
current = parent / "current"
child = current / "child"
parent.mkdir()
current.mkdir()
child.mkdir()
navigator = Navigator(current)
navigator.set_selected_index(1)
ui = TerminalUI()
assert ui._handle_tree_key(curses.KEY_LEFT, navigator) is None
assert navigator.current_path == parent.resolve()
assert navigator.selected_index == 0
def test_enter_returns_selected_path_without_navigating(tmp_path: Path) -> None:
workspace = tmp_path / "workspace"
child = workspace / "child"
workspace.mkdir()
child.mkdir()
navigator = Navigator(workspace)
navigator.set_selected_index(1)
ui = TerminalUI()
result = ui._handle_tree_key(10, navigator)
assert result == child.resolve()
assert navigator.current_path == workspace.resolve()
def test_open_terminal_streams_raises_without_terminal(
monkeypatch: pytest.MonkeyPatch,
) -> None: