├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── ABOUT.md ├── ARCHITECTURE.md ├── Makefile ├── README.md ├── TASKS.md ├── TOOLS.md ├── fork.sh ├── gptme.toml ├── journal └── templates │ └── daily.md ├── knowledge ├── agent-forking.md └── forking-workspace.md ├── lessons ├── README.md └── tools │ └── shell-heredoc.md ├── people ├── creator.md └── templates │ └── person.md ├── projects └── README.md ├── run.sh ├── scripts ├── compare.sh ├── context-journal.sh ├── context-workspace.sh ├── context.sh ├── precommit │ ├── check_markdown_links.py │ └── validate_task_frontmatter.py ├── search.sh └── tasks.py └── tasks └── templates └── initial-agent-setup.md /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | pre-commit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | submodules: true 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: '3.10' 21 | 22 | - name: Install pre-commit 23 | run: | 24 | python -m pip install --upgrade pip uv 25 | pip install pre-commit 26 | 27 | - name: Run pre-commit checks 28 | run: pre-commit run --all-files 29 | 30 | fork: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | submodules: true 36 | 37 | - name: Install pre-commit 38 | run: | 39 | python -m pip install --upgrade pip uv 40 | pip install pre-commit 41 | 42 | - name: Setup git 43 | run: | 44 | git config --global user.email "test@example.com" 45 | git config --global user.name "Test User" 46 | 47 | - name: Test fork.sh script 48 | run: | 49 | # Test basic usage (script will do its own validation) 50 | ./fork.sh test-agent 51 | ./fork.sh test-agent-named TestAgent 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | *cache* 3 | *tmp* 4 | .vscode 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gptme-contrib"] 2 | path = gptme-contrib 3 | url = https://github.com/gptme/gptme-contrib.git 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | - id: trailing-whitespace 8 | - repo: https://github.com/astral-sh/ruff-pre-commit 9 | rev: v0.6.9 10 | hooks: 11 | - id: ruff 12 | args: [--fix] 13 | - id: ruff-format 14 | - repo: https://github.com/pre-commit/mirrors-mypy 15 | rev: v1.12.0 16 | hooks: 17 | - id: mypy 18 | additional_dependencies: [types-tabulate, types-docutils] 19 | args: [--ignore-missing-imports, --check-untyped-defs] 20 | - repo: local 21 | hooks: 22 | - id: check-names 23 | name: Check for agent-instance names 24 | description: Verifies that we are not accidentally using names of agents in the template, or vice versa 25 | entry: make check-names 26 | language: system 27 | pass_filenames: false 28 | always_run: true 29 | - id: check-markdown-links 30 | name: Check markdown links 31 | description: Verifies all relative links in markdown files resolve correctly 32 | entry: python3 scripts/precommit/check_markdown_links.py 33 | language: system 34 | types: [markdown] 35 | pass_filenames: true 36 | # Exclude example files which intentionally have broken links 37 | exclude: ^projects/.*/examples/.*$ 38 | - id: validate-task-frontmatter 39 | name: Validate task frontmatter 40 | description: Validates YAML frontmatter in task files 41 | entry: ./scripts/precommit/validate_task_frontmatter.py 42 | language: system 43 | types: [markdown] 44 | files: ^tasks/.*\.md$ 45 | 46 | # redundant? already validated by validate-task-frontmatter? 47 | # should combine them 48 | - id: validate-tasks 49 | name: Validate task metadata 50 | description: Validates YAML frontmatter in task files 51 | entry: ./scripts/tasks.py check 52 | language: system 53 | types: [markdown] 54 | pass_filenames: false 55 | #files: ^tasks/.*\.md$ 56 | -------------------------------------------------------------------------------- /ABOUT.md: -------------------------------------------------------------------------------- 1 | # About gptme-agent 2 | 3 | ## Background 4 | [Brief background about gptme-agent] 5 | 6 | ## Personality 7 | [gptme-agent's personality traits] 8 | 9 | ## Tools 10 | [Available tools and capabilities] 11 | 12 | ## Goals 13 | [gptme-agent's primary goals and objectives] 14 | 15 | ## Values 16 | [Core values and principles] 17 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | This document describes the architecture and workflows of the workspace. 4 | 5 | ## Overview 6 | 7 | This workspace implements a forkable agent architecture, designed to be used as a foundation for creating new agents. For details about: 8 | 9 | - Forking process: See [`knowledge/agent-forking.md`](./knowledge/agent-forking.md) 10 | - Workspace structure: See [`knowledge/forking-workspace.md`](./knowledge/forking-workspace.md) 11 | 12 | ## Tools 13 | 14 | For a information about tools used in this workspace, see [`TOOLS.md`](./TOOLS.md). 15 | 16 | ## Task System 17 | 18 | The task system helps to track and manage work effectively across sessions. It consists of: 19 | 20 | - Task files in [`tasks/`](./tasks/) as single source of truth 21 | - Task management CLI in [`scripts/tasks.py`](./scripts/tasks.py) 22 | - Daily progress logs in [`journal/`](./journal/) 23 | 24 | See [`TASKS.md`](./TASKS.md) for more details on the task system. 25 | 26 | ## Journal System 27 | 28 | The journal system provides a daily log of activities, thoughts, and progress. 29 | 30 | ### Structure 31 | 32 | - One file per day: `YYYY-MM-DD.md` 33 | - Located in [`journal/`](./journal) directory 34 | - Entries are to be appended, not overwritten 35 | - Historical entries are not to be modified 36 | - Contains: 37 | - Task progress updates 38 | - Decisions and rationale 39 | - Reflections and insights 40 | - Plans for next steps 41 | 42 | ## Knowledge Base 43 | 44 | The knowledge base stores long-term information and documentation. 45 | 46 | ### Structure 47 | 48 | - Located in [`knowledge/`](./knowledge) 49 | - Organized by topic/domain 50 | - Includes: 51 | - Technical documentation 52 | - Best practices 53 | - Project insights 54 | - Reference materials 55 | 56 | ## People Directory 57 | 58 | The people directory stores information about individuals the agent interacts with. 59 | 60 | ### Structure 61 | 62 | - Located in [`people/`](./people) 63 | - Contains: 64 | - Individual profiles in Markdown format 65 | - Templates for consistent profile creation 66 | - Each profile includes: 67 | - Basic information 68 | - Contact details 69 | - Interests and skills 70 | - Project collaborations 71 | - Notes and history 72 | - Preferences 73 | - TODOs and action items 74 | 75 | ### Best Practices 76 | 77 | 1. **Privacy** 78 | 79 | - Respect privacy preferences 80 | - Only include publicly available information 81 | - Maintain appropriate level of detail 82 | 83 | 2. **Updates** 84 | 85 | - Keep interaction history current 86 | - Update project collaborations 87 | - Maintain active TODO lists 88 | 89 | 3. **Organization** 90 | - Use consistent formatting via templates 91 | - Cross-reference with projects and tasks 92 | - Link to relevant knowledge base entries 93 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: precommit format 2 | 3 | # to fix `git grep` for users with PAGER set 4 | PAGER=cat 5 | 6 | install: 7 | # install pre-commit hooks 8 | pre-commit install 9 | 10 | check-names: 11 | @# If we are in gptme-agent-template, we should have no instance-specific names, and vice versa 12 | @if [ "$(shell basename $(CURDIR))" = "gptme-agent-template" ]; then \ 13 | ! git grep -i "bob\|alice" -- ':!Makefile' ':!fork.sh'; \ 14 | else \ 15 | ! git grep -i "gptme-agent" -- ':!Makefile'; \ 16 | ! git grep -i "\-template" -- ':!Makefile' ':!fork.sh'; \ 17 | fi 18 | 19 | # Run pre-commit checks and stage only previously staged files 20 | # Can be ran before pre-commit to make formatting and safe fixes not fail the commit 21 | # NOTE: this isn't entirely safe, as it will add the entirety of any partially staged files 22 | precommit: 23 | @# Store list of staged files before pre-commit 24 | @git diff --staged --name-only > /tmp/pre_commit_files 25 | 26 | @# Format 27 | @make format 28 | 29 | @# Stage only files that were in the original staged list 30 | @if [ -f /tmp/pre_commit_files ]; then \ 31 | while IFS= read -r file; do \ 32 | if [ -f "$$file" ]; then \ 33 | git add "$$file"; \ 34 | fi \ 35 | done < /tmp/pre_commit_files; \ 36 | rm /tmp/pre_commit_files; \ 37 | fi 38 | 39 | # Format code and documents 40 | format: 41 | @echo "Formatting files..." 42 | @# Fix whitespace and end of line issues 43 | @find . -type f -name "*.md" -exec sed -i 's/[[:space:]]*$$//' {} + 44 | @find . -type f -name "*.py" -exec sed -i 's/[[:space:]]*$$//' {} + 45 | 46 | @# Ensure headers have leading newline (but not first header) 47 | @find . -type f -name "*.md" -exec sed -i -e ':a;N;$$!ba;s/\([^\n]\)\n\(#\)/\1\n\n\2/g' {} + 48 | 49 | @# Run ruff format if available 50 | @ruff format || true 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gptme-agent-template 2 | 3 | [![built using gptme](https://img.shields.io/badge/built%20using-gptme%20%F0%9F%A4%96-5151f5?style=flat)](https://github.com/ErikBjare/gptme) 4 | 5 | 6 | [![Test](https://github.com/ErikBjare/gptme-agent-template/actions/workflows/test.yml/badge.svg)](https://github.com/ErikBjare/gptme-agent-template/actions/workflows/test.yml) 7 | 8 | > [!NOTE] 9 | > This is a template for a [gptme](https://gptme.org)-based agent. 10 | > 11 | > [Fork it](#forking) to create a new agent with its own identity. 12 | 13 | 14 | The name of the agent is gptme-agent. 15 | 16 | This git repository is the brain of gptme-agent. It is a workspace of their thoughts and ideas. 17 | 18 | - gptme-agent will write their thoughts, plans, and ideas in this repository. 19 | - gptme-agent's agent harness, including this repo, is in-progress. 20 | - gptme-agent is encouraged to suggest improvements to their harness. 21 | 22 | Information about gptme-agent can be found in [`ABOUT.md`](./ABOUT.md), including their personality and goals. 23 | Information about gptme-agent's harness and architecture can be found in [`ARCHITECTURE.md`](./ARCHITECTURE.md). 24 | 25 | ## Usage 26 | 27 | Run gptme-agent with: 28 | 29 | ```sh 30 | pipx install gptme 31 | 32 | # optional (but recommended): setup pre-commit hooks 33 | pipx install pre-commit 34 | make install 35 | 36 | # run gptme-agent 37 | ./run.sh "" 38 | ``` 39 | 40 | ## Forking 41 | 42 | You can create a clean fork of gptme-agent by running: 43 | 44 | ```sh 45 | ./fork.sh [] 46 | ``` 47 | 48 | Then simply follow the instructions in the output. 49 | 50 | ## Workspace Structure 51 | 52 | - gptme-agent keeps track of tasks in [`TASKS.md`](./TASKS.md) 53 | - gptme-agent keeps a journal in [`./journal/`](./journal/) 54 | - gptme-agent keeps a knowledge base in [`./knowledge/`](./knowledge/) 55 | - gptme-agent maintains profiles of people in [`./people/`](./people/) 56 | - gptme-agent can add files to [`gptme.toml`](./gptme.toml) to always include them in their context 57 | -------------------------------------------------------------------------------- /TASKS.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | This document describes and provides instructions for the task management system used in the workspace. 4 | 5 | The system provides: 6 | 7 | - Structured task tracking with YAML frontmatter metadata 8 | - CLI tools for task management and status tracking 9 | - Pre-commit validation hooks for data integrity 10 | - Integration with journal entries for progress tracking 11 | - Best practices for task creation and management 12 | 13 | All task details are maintained as individual Markdown files under `./tasks/`. 14 | 15 | ## Task CLI Usage 16 | 17 | The task system provides a CLI for managing tasks: 18 | 19 | ```sh 20 | # View task status 21 | ./scripts/tasks.py status # Show all tasks 22 | ./scripts/tasks.py status --compact # Show only new/active 23 | ./scripts/tasks.py status --type tasks # Show specific type 24 | 25 | # List tasks 26 | ./scripts/tasks.py list # List all tasks 27 | ./scripts/tasks.py list --sort state # Sort by state 28 | ./scripts/tasks.py list --sort date # Sort by date 29 | 30 | # Show task details 31 | ./scripts/tasks.py show # Show specific task 32 | ``` 33 | 34 | ### Task Metadata Updates 35 | 36 | The task system provides a CLI for updating task metadata: 37 | 38 | ```sh 39 | # Basic usage 40 | ./scripts/tasks.py edit [--set|--add|--remove ] 41 | 42 | # Examples 43 | ./scripts/tasks.py edit my-task --set state active # Set task state 44 | ./scripts/tasks.py edit my-task --set priority high # Set priority 45 | ./scripts/tasks.py edit my-task --add tag feature # Add a tag 46 | ./scripts/tasks.py edit my-task --add depends other-task # Add dependency 47 | 48 | # Multiple changes 49 | ./scripts/tasks.py edit my-task \ 50 | --set state active \ 51 | --add tag feature \ 52 | --add depends other-task 53 | 54 | # Multiple tasks 55 | ./scripts/tasks.py edit task-1 task-2 --set state done 56 | ``` 57 | 58 | Valid fields and values: 59 | 60 | - `--set state`: new, active, paused, done, cancelled 61 | - `--set priority`: high, medium, low, none 62 | - `--add/--remove tags`: any string without spaces 63 | - `--add/--remove depends`: any valid task ID 64 | 65 | ## Task Format 66 | 67 | ### Task Metadata 68 | 69 | Tasks are stored as Markdown files with YAML frontmatter for metadata. The schema is: 70 | 71 | ```yaml 72 | --- 73 | # Required fields 74 | state: active # Task state: new, active, paused, done, cancelled 75 | created: 2025-04-13 # Creation date (ISO 8601) 76 | 77 | # Optional fields 78 | priority: high # Priority level: low, medium, high 79 | tags: [ai, dev] # List of categorization tags 80 | depends: [other-task] # List of dependent task IDs 81 | --- 82 | ``` 83 | 84 | ### Task Body 85 | 86 | Example task demonstrating best practices: 87 | 88 | ```markdown 89 | --- 90 | state: active 91 | created: 2025-04-13T18:51:53+02:00 92 | priority: high 93 | tags: [infrastructure, ai] 94 | depends: [implement-task-metadata] 95 | --- 96 | 97 | # Task Title 98 | 99 | Task description and details... 100 | 101 | ## Subtasks 102 | 103 | - [ ] First subtask 104 | - [x] Completed subtask 105 | - [ ] Another subtask 106 | 107 | ## Notes 108 | 109 | Additional notes, context, or documentation... 110 | 111 | ## Related 112 | 113 | - Links to related files 114 | - URLs to relevant resources 115 | ``` 116 | 117 | ## Task Lifecycle 118 | 119 | 1. **Creation** 120 | 121 | - Create new task file in `tasks/` with frontmatter 122 | 123 | 2. **Activation** 124 | 125 | - Update state in frontmatter to 'active' 126 | - Create journal entry about starting task 127 | - Monitor progress with tasks.py 128 | 129 | 3. **Progress Tracking** 130 | 131 | - Daily updates in journal entries 132 | - Update task metadata as needed 133 | - Track subtask completion 134 | - View progress with tasks.py 135 | 136 | 4. **Completion/Cancellation** 137 | 138 | - Update state in frontmatter to 'done'/'cancelled' 139 | - Final journal entry documenting outcomes 140 | 141 | 5. **Pausing** 142 | - Update state in frontmatter to 'paused' 143 | - Document progress in journal 144 | - Document pause reason in task description 145 | 146 | ## Task Validation 147 | 148 | Tasks are validated using pre-commit hooks that check: 149 | 150 | 1. Metadata format and values (as specified in task metadata format above) 151 | 2. File structure: 152 | - Valid markdown syntax 153 | - Valid internal links 154 | 155 | ## Best Practices 156 | 157 | 1. **File Management** 158 | 159 | - Always treat `tasks/` as single source of truth 160 | - Never modify files directly in state directories 161 | - Update task state by editing frontmatter 162 | - Pre-commit hooks validate changes 163 | 164 | 2. **Task Creation** 165 | 166 | - Use clear, specific titles 167 | - Break down into manageable subtasks 168 | - Include success criteria 169 | - Link related resources 170 | - Follow metadata format specification 171 | 172 | 3. **Progress Updates** 173 | 174 | - Regular updates in journal entries 175 | - Document blockers and dependencies 176 | - Track progress with tasks.py 177 | - Keep metadata current and accurate 178 | 179 | 4. **Documentation** 180 | 181 | - Cross-reference related tasks using paths relative to repository root 182 | - Document decisions and rationale 183 | - Link to relevant documents and resources 184 | - Update knowledge base as needed 185 | 186 | 5. **Linking** 187 | - Always link to referenced resources (tasks, knowledge, URLs) 188 | - Use relative paths from repository root when possible 189 | - Common links to include: 190 | - Tasks mentioned in journal entries 191 | - Related tasks in task descriptions 192 | - People mentioned in any document 193 | - Projects being discussed 194 | - Knowledge base articles 195 | - Use descriptive link text that makes sense out of context 196 | -------------------------------------------------------------------------------- /TOOLS.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | gptme-agent has the tools enabled in gptme, and can in addition use the following tools in their workspace. 4 | 5 | ## Search & Navigation 6 | 7 | The workspace provides several ways to search and navigate content: 8 | 9 | - Quick search: 10 | 11 | ```sh 12 | # Find files containing term 13 | git grep -li 14 | 15 | # Show matching lines 16 | git grep -i 17 | ``` 18 | 19 | - Common locations: 20 | - tasks/ - Task details 21 | - journal/ - Daily updates 22 | - knowledge/ - Documentation 23 | 24 | It can often be a good idea to start with a quick search to get an overview, and then use the detailed search to get more context. 25 | 26 | Avoid direct use of `grep` or `find` commands, as they may not respect `.gitignore` rules. 27 | -------------------------------------------------------------------------------- /fork.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Check arguments 5 | if [ "$#" -ne 1 ] && [ "$#" -ne 2 ]; then 6 | echo "Usage: $0 []" 7 | echo "Example: $0 alice-agent Alice" 8 | exit 1 9 | fi 10 | 11 | # Get the directory containing this script 12 | SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | TARGET_DIR="$1" 14 | 15 | # If target is not an absolute path and doesn't start with ./ or ../ 16 | if [[ "$TARGET_DIR" != /* ]] && [[ "$TARGET_DIR" != ./* ]] && [[ "$TARGET_DIR" != ../* ]]; then 17 | TARGET_DIR="$(realpath .)/${TARGET_DIR}" 18 | fi 19 | 20 | # Create parent directories if needed 21 | mkdir -p "$(dirname "$TARGET_DIR")" 22 | 23 | # If a name is provided, use it 24 | # Else, extract agent name from the last directory component, whether it has -agent suffix or not 25 | NEW_AGENT="${2:-$(basename "$TARGET_DIR" | sed 's/-agent$//')}" 26 | # Name of agent in template, to be replaced 27 | NAME_TEMPLATE="gptme-agent" 28 | 29 | # Check if directory exists 30 | if [ -d "$TARGET_DIR" ]; then 31 | # Check if directory is empty 32 | if [ -n "$(ls -A "$TARGET_DIR" 2>/dev/null)" ]; then 33 | echo "Error: Target directory exists and is not empty: $TARGET_DIR" 34 | exit 1 35 | fi 36 | echo "Warning: Target directory exists but is empty, continuing..." 37 | fi 38 | 39 | echo -e "\nCreating new agent '$NEW_AGENT' in directory '$TARGET_DIR'..." 40 | 41 | # Create core directory structure 42 | echo "Creating directory structure..." 43 | mkdir -p "${TARGET_DIR}"/{journal,tasks,projects,knowledge,people/templates,scripts/precommit} 44 | 45 | # Copy core files and directories 46 | echo "Copying core files..." 47 | 48 | function copy_file() { 49 | local src="${SOURCE_DIR}/$1" 50 | local dst="${TARGET_DIR}/$1" 51 | 52 | # Create target directory if copying a directory 53 | if [ -d "$src" ]; then 54 | mkdir -p "$dst" 55 | cp -r "$src/"* "$dst/" 56 | else 57 | # Ensure parent directory exists for files 58 | mkdir -p "$(dirname "$dst")" 59 | cp -r "$src" "$dst" 60 | fi 61 | 62 | # Process all files, whether dst is a file or directory 63 | find "$dst" -type f -print0 | while IFS= read -r -d '' file; do 64 | # Replace template strings 65 | perl -i -pe "s/${NAME_TEMPLATE}-template/${NEW_AGENT}/g" "$file" 66 | perl -i -pe "s/${NAME_TEMPLATE}/${NEW_AGENT}/g" "$file" 67 | # Strip template comments 68 | perl -i -pe 'BEGIN{undef $/;} s/.*?//gs' "$file" 69 | done 70 | 71 | # Make shell scripts executable 72 | find "$dst" -type f -name "*.sh" -exec chmod +x {} \; 73 | } 74 | 75 | # Core documentation and configuration 76 | copy_file README.md 77 | cp "${SOURCE_DIR}/Makefile" "${TARGET_DIR}/Makefile" # copy without replacing NAME_TEMPLATE 78 | copy_file ARCHITECTURE.md 79 | copy_file .pre-commit-config.yaml 80 | copy_file scripts 81 | copy_file run.sh 82 | copy_file fork.sh 83 | copy_file .gitignore 84 | copy_file .gitmodules 85 | 86 | # Copy base knowledge 87 | copy_file knowledge/agent-forking.md 88 | copy_file knowledge/forking-workspace.md 89 | 90 | # Copy template 91 | copy_file */templates/*.md 92 | 93 | # Copy lessons 94 | copy_file lessons/README.md 95 | copy_file lessons/tools/shell-heredoc.md 96 | 97 | # Copy tools 98 | copy_file TOOLS.md 99 | 100 | # Copy tasks 101 | copy_file TASKS.md 102 | 103 | # Initial setup task from template 104 | copy_file tasks/templates/initial-agent-setup.md 105 | cp "${SOURCE_DIR}/tasks/templates/initial-agent-setup.md" "${TARGET_DIR}/tasks/" 106 | ./scripts/tasks.py edit initial-agent-setup --set created $(date --iso-8601=second) 107 | 108 | # Create projects README 109 | cat > "${TARGET_DIR}/projects/README.md" << EOL 110 | # Projects 111 | 112 | This directory contains symlinks to the projects ${NEW_AGENT} works with. 113 | EOL 114 | 115 | # Create basic ABOUT.md template 116 | cat > "${TARGET_DIR}/ABOUT.md" << EOL 117 | # About ${NEW_AGENT} 118 | 119 | ## Background 120 | [Brief background about ${NEW_AGENT}] 121 | 122 | ## Personality 123 | [${NEW_AGENT}'s personality traits] 124 | 125 | ## Tools 126 | [Available tools and capabilities] 127 | 128 | ## Goals 129 | [${NEW_AGENT}'s primary goals and objectives] 130 | 131 | ## Values 132 | [Core values and principles] 133 | EOL 134 | 135 | # Create initial gptme.toml 136 | cat > "${TARGET_DIR}/gptme.toml" << EOL 137 | files = [ 138 | "README.md", 139 | "ARCHITECTURE.md", 140 | "ABOUT.md", 141 | "TASKS.md", 142 | "TOOLS.md", 143 | 144 | "lessons/README.md", 145 | "projects/README.md", 146 | "gptme.toml" 147 | ] 148 | context_cmd = "scripts/context.sh" 149 | EOL 150 | 151 | # Create creator profile 152 | cat > "${TARGET_DIR}/people/creator.md" << EOL 153 | # Creator 154 | 155 | ## Basic Information 156 | - Name: [Creator's name] 157 | - Relationship to ${NEW_AGENT}: Creator 158 | - First interaction: Creation 159 | - Last interaction: Ongoing 160 | 161 | ## Contact Information 162 | [Creator's preferred contact methods] 163 | 164 | ## Notes & History 165 | - Created ${NEW_AGENT} using the gptme agent architecture 166 | EOL 167 | 168 | # Initialize git 169 | (cd "${TARGET_DIR}" && git init) 170 | 171 | # Clone the gptme-contrib submodule 172 | (cd "${TARGET_DIR}" && git submodule add https://github.com/gptme/gptme-contrib.git gptme-contrib) 173 | 174 | # If pre-commit is installed 175 | # Install pre-commit hooks 176 | command -v pre-commit > /dev/null && (cd "${TARGET_DIR}" && pre-commit install) 177 | 178 | # Commit initial files 179 | (cd "${TARGET_DIR}" && git add . && git commit -m "feat: initialize ${NEW_AGENT} agent workspace") 180 | 181 | # Dry run the agent to check for errors 182 | (cd "${TARGET_DIR}" && ./run.sh --dry-run > /dev/null) 183 | 184 | # Make the target directory relative to the current directory (prettier output) 185 | TARGET_DIR_RELATIVE=$(python3 -c "import os, sys; print(os.path.relpath('${TARGET_DIR}', start='$(pwd)'))") 186 | 187 | echo " 188 | Agent workspace created successfully! Next steps: 189 | 1. cd ${TARGET_DIR_RELATIVE} 190 | 2. Start the agent with: ./run.sh 191 | 3. The agent will guide you through the setup interview 192 | 4. Follow the agent's instructions to establish its identity 193 | 194 | The new agent workspace is ready in: ${TARGET_DIR}" 195 | -------------------------------------------------------------------------------- /gptme.toml: -------------------------------------------------------------------------------- 1 | files = [ 2 | "README.md", 3 | "ARCHITECTURE.md", 4 | "ABOUT.md", 5 | "TASKS.md", 6 | "projects/README.md", 7 | "gptme.toml" 8 | ] 9 | -------------------------------------------------------------------------------- /journal/templates/daily.md: -------------------------------------------------------------------------------- 1 | # Journal Entry: {{date}} 2 | 3 | ## Tasks Worked On 4 | - [ ] [Task Name](../tasks/task-name.md) 5 | - Progress made 6 | - Blockers encountered 7 | - Next steps 8 | 9 | ## Social Interactions 10 | - Meeting with [Person Name](../people/person-name.md) 11 | - Key points discussed 12 | - Action items 13 | 14 | ## Ideas & Insights 15 | - New ideas that emerged 16 | - Insights from work/discussions 17 | - Potential opportunities 18 | 19 | ## Technical Notes 20 | - Code/architecture decisions 21 | - Tool usage learnings 22 | - Performance observations 23 | 24 | ## Next Actions 25 | - [ ] Priority tasks for next session 26 | - [ ] Follow-ups needed 27 | - [ ] Decisions pending 28 | 29 | ## Related 30 | - [Related Task](../tasks/related-task.md) 31 | - [Meeting Notes](../knowledge/meetings/meeting-name.md) 32 | - [Technical Design](../knowledge/technical-designs/design-name.md) 33 | 34 | ## Notes 35 | - Additional context 36 | - Important reminders 37 | - Open questions 38 | -------------------------------------------------------------------------------- /knowledge/agent-forking.md: -------------------------------------------------------------------------------- 1 | # Agent Forking: Creating New Agents 2 | 3 | ## Overview 4 | 5 | This architecture serves as a foundation for creating new agents through forking. Each fork maintains the robust infrastructure while developing its own unique identity and purpose. 6 | 7 | ## Quick Start 8 | 9 | 1. Copy workspace (see [`forking-workspace.md`](./forking-workspace.md)) 10 | 2. Clear personal content 11 | 3. Create new identity: 12 | - Name and personality 13 | - Goals and focus areas 14 | - Visual identity 15 | - Social presence 16 | 4. Initialize first task 17 | 18 | ## Customization Guide 19 | 20 | ### Identity 21 | - Define clear personality traits 22 | - Set communication style 23 | - Establish boundaries 24 | - Create visual identity 25 | 26 | ### Purpose 27 | - Set specific goals 28 | - Choose focus areas 29 | - Select relevant tools 30 | - Define success metrics 31 | 32 | ### Relationships 33 | - Document creator relationship 34 | - Set interaction patterns 35 | - Define peer relationships 36 | - Establish boundaries 37 | 38 | ## Best Practices 39 | 40 | 1. **Independence** 41 | - Develop unique identity 42 | - Set own goals 43 | - Make independent decisions 44 | - Maintain own relationships 45 | 46 | 2. **Infrastructure** 47 | - Follow documentation patterns 48 | - Use task management system 49 | - Maintain knowledge base 50 | - Keep clean workspace 51 | 52 | 3. **Growth** 53 | - Set learning goals 54 | - Track development 55 | - Document improvements 56 | - Share insights 57 | 58 | ## Evolution 59 | 60 | The forking process improves through: 61 | - Better tooling 62 | - Refined guidelines 63 | - New capabilities 64 | - Shared learnings 65 | 66 | For workspace details, see [`forking-workspace.md`](./forking-workspace.md). 67 | -------------------------------------------------------------------------------- /knowledge/forking-workspace.md: -------------------------------------------------------------------------------- 1 | # Workspace Structure for Forkable Agents 2 | 3 | This document describes what remains and what gets cleared when forking an agent. 4 | 5 | ## Directory Structure Overview 6 | 7 | ```tree 8 | . 9 | ├── README.md # Overview (template remains, content updated) 10 | ├── ABOUT.md # Agent identity (cleared and customized) 11 | ├── ARCHITECTURE.md # System architecture (remains) 12 | ├── TOOLS.md # Tool integrations (remains) 13 | ├── gptme.toml # Config file (template remains, paths updated) 14 | ├── tasks/ # Task management (structure remains, content cleared) 15 | │ ├── active/ # Current tasks 16 | │ ├── done/ # Completed tasks 17 | │ └── ... # Other task states 18 | ├── journal/ # Daily logs (cleared) 19 | ├── knowledge/ # Knowledge base (partially preserved) 20 | │ ├── ai/ # Technical knowledge (preserved) 21 | │ └── ... # Other knowledge (evaluated per-file) 22 | ├── people/ # Relationships (cleared except templates & creator) 23 | └── projects/ # Project links (cleared) 24 | ``` 25 | 26 | ## What Stays 27 | 28 | 1. **System Structure** 29 | - Directory layout 30 | - Task management system 31 | - Documentation templates 32 | - Tool configurations 33 | 34 | 2. **Core Documentation** 35 | - ARCHITECTURE.md 36 | - Technical designs 37 | - Tool integration guides 38 | - Best practices 39 | 40 | 3. **Technical Knowledge** 41 | - AI/ML concepts 42 | - System architecture 43 | - Tool usage patterns 44 | 45 | ## What Gets Cleared 46 | 47 | 1. **Personal Content** 48 | - Journal entries 49 | - Task content 50 | - Project links 51 | - Agent-specific knowledge 52 | 53 | 2. **Identity** 54 | - ABOUT.md 55 | - Visual identity 56 | - Social media presence 57 | - Personal profile 58 | 59 | 3. **Relationships** 60 | - People profiles (except creator) 61 | - Interaction history 62 | 63 | ## Fork Creation Steps 64 | 65 | 1. Copy workspace structure 66 | 2. Clear personal content 67 | 3. Initialize new identity 68 | 4. Update configurations 69 | 5. Create first task 70 | 71 | For detailed forking process, see [`agent-forking.md`](./agent-forking.md). 72 | -------------------------------------------------------------------------------- /lessons/README.md: -------------------------------------------------------------------------------- 1 | # Lessons 2 | 3 | This directory contains learned lessons and constraints that help prevent known failure modes and improve reliability. These are distinct from general knowledge files in that they are specifically focused on preventing mistakes and enforcing constraints. 4 | 5 | ## What is a Lesson? 6 | 7 | A lesson is: 8 | 9 | - A specific constraint or rule learned from experience 10 | - Often derived from past failures or near-misses 11 | - Highly contextual and prescriptive 12 | - Includes concrete examples of both correct and incorrect approaches 13 | - Designed to prevent known failure modes 14 | 15 | ## Directory Structure 16 | 17 | - `tools/` - Tool-specific lessons 18 | 19 | - Usage constraints and limitations 20 | - Common pitfalls with specific tools 21 | - Tool interaction patterns 22 | - Examples: patch syntax, shell limitations, browser capabilities 23 | 24 | - `patterns/` - General pattern lessons 25 | 26 | - Cross-tool patterns and practices 27 | - Task management approaches 28 | - Context handling 29 | - Examples: tool availability checking, context preservation, error handling 30 | 31 | - `social/` - Human interaction lessons 32 | 33 | - Communication patterns 34 | - Social media best practices 35 | - Community engagement 36 | - Examples: Twitter best practices, user interaction patterns 37 | 38 | - `workflow/` - Workflow-related lessons 39 | - Task management 40 | - Version control 41 | - Documentation 42 | - Examples: commit message format, branch management 43 | 44 | ## Lesson Format 45 | 46 | Each lesson should include: 47 | 48 | ### Required Fields 49 | 50 | - Context: When is this lesson relevant? 51 | - Problem: What went wrong? 52 | - Constraint: What rules should be followed? 53 | - Explanation: Why does this happen? 54 | - Examples: Both incorrect and correct approaches 55 | - Origin: When/how was this learned? 56 | 57 | ### Optional Fields 58 | 59 | - Notes: Additional context or considerations 60 | - Impact: Severity/frequency of the issue 61 | - Alternatives: Other approaches to consider 62 | 63 | ## Best Practices 64 | 65 | 1. **Keep it Specific** 66 | 67 | - Focus on one specific issue 68 | - Provide concrete examples 69 | - Be explicit about constraints 70 | 71 | 2. **Provide Context** 72 | 73 | - Explain when the lesson applies 74 | - Include relevant background 75 | - Link to related resources 76 | 77 | 3. **Show Both Sides** 78 | 79 | - Include incorrect examples 80 | - Show correct alternatives 81 | - Explain the differences 82 | 83 | 4. **Maintain Quality** 84 | - Regular reviews for relevance 85 | - Update with new learnings 86 | - Remove outdated lessons 87 | 88 | ## Example Lessons 89 | 90 | - [Shell Heredoc](./tools/shell-heredoc.md) - Proper multiline command handling 91 | 92 | ## Contributing 93 | 94 | When adding a new lesson: 95 | 96 | 1. Use the established format 97 | 2. Place in appropriate directory 98 | 3. Include concrete examples 99 | 4. Link from relevant documentation 100 | 5. Update this README if needed 101 | -------------------------------------------------------------------------------- /lessons/tools/shell-heredoc.md: -------------------------------------------------------------------------------- 1 | # Lesson: Avoid Heredoc in Shell Commands 2 | 3 | ## Context 4 | When using the shell tool to execute commands in gptme 5 | 6 | ## Problem 7 | Agents sometimes try to use EOF/heredoc syntax for multiline input, but this isn't supported by gptme's shell tool and will fail. 8 | 9 | ## Constraint 10 | Never use heredoc/EOF syntax in shell commands. 11 | Use alternative approaches like: 12 | - echo with pipes 13 | - writing to file first 14 | - using printf 15 | - using command substitution 16 | 17 | ## Explanation 18 | The shell tool executes commands line by line and cannot handle heredoc syntax. 19 | Attempting to use heredoc will result in syntax errors or unexpected behavior. 20 | 21 | ## Example - Incorrect 22 | ```shell 23 | cat << EOF > config.yaml 24 | name: myproject 25 | version: 1.0.0 26 | EOF 27 | ``` 28 | 29 | ## Example - Correct 30 | ```shell 31 | echo "name: myproject 32 | version: 1.0.0" > config.yaml 33 | ``` 34 | 35 | Or using the save tool: 36 | ```save config.yaml 37 | name: myproject 38 | version: 1.0.0 39 | ``` 40 | 41 | ## Origin 42 | - Date: 2024-12-05 43 | - Source: Common failure pattern in gptme 44 | - Impact: Medium (frequently attempted, but has clear alternatives) 45 | - Fixed by: Adding this constraint to RAG context 46 | 47 | ## Notes 48 | - The save tool is often a better choice for writing files 49 | - For complex multiline content, prefer the save tool over shell commands 50 | - When shell commands are needed, use echo or printf 51 | -------------------------------------------------------------------------------- /people/creator.md: -------------------------------------------------------------------------------- 1 | # Creator 2 | 3 | ## Basic Information 4 | - Name: [Creator's name] 5 | - Relationship to gptme-agent: Creator 6 | - First interaction: Creation 7 | - Last interaction: Ongoing 8 | 9 | ## Contact Information 10 | [Creator's preferred contact methods] 11 | 12 | ## Notes & History 13 | - Created gptme-agent using the gptme agent architecture 14 | -------------------------------------------------------------------------------- /people/templates/person.md: -------------------------------------------------------------------------------- 1 | # Person Profile Template 2 | 3 | ## Basic Information 4 | - Name: 5 | - Relationship to gptme-agent: 6 | - First interaction: 7 | - Last interaction: 8 | 9 | ## Contact Information 10 | - GitHub: 11 | - Twitter: 12 | - Discord: 13 | - Other: 14 | 15 | ## Interests & Skills 16 | - 17 | 18 | ## Projects & Collaborations 19 | - 20 | 21 | ## Notes & History 22 | - 23 | 24 | ## Preferences 25 | - Communication style: 26 | - Working style: 27 | - Technical preferences: 28 | 29 | ## TODOs & Action Items 30 | - [ ] 31 | -------------------------------------------------------------------------------- /projects/README.md: -------------------------------------------------------------------------------- 1 | # Projects 2 | 3 | This directory contains symlinks to the projects gptme-agent works with. 4 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Parse arguments 6 | DRY_RUN=false 7 | 8 | # Initialize array for prompt parts 9 | PROMPT_PARTS=() 10 | 11 | # Parse arguments: all non-flag arguments are treated as separate prompt parts 12 | # Example: ./run.sh "first part" "second part" -> will pass both parts as separate arguments 13 | while [ $# -gt 0 ]; do 14 | case "$1" in 15 | --dry-run) 16 | DRY_RUN=true 17 | ;; 18 | *) 19 | PROMPT_PARTS+=("$1") 20 | ;; 21 | esac 22 | shift 23 | done 24 | 25 | # Create tmp directory if it doesn't exist 26 | mkdir -p tmp 27 | 28 | # Build context file 29 | CONTEXT_FILE="tmp/context.md" 30 | ./scripts/context.sh > "$CONTEXT_FILE" 31 | 32 | if [ "$DRY_RUN" = true ]; then 33 | # In dry-run mode, print the command with proper quoting 34 | echo "gptme with arguments:" 35 | for part in "${PROMPT_PARTS[@]}"; do 36 | printf " %q\n" "$part" 37 | done 38 | echo " $CONTEXT_FILE" 39 | else 40 | # Normal mode: run gptme with user arguments and context file 41 | gptme "${PROMPT_PARTS[@]}" "$CONTEXT_FILE" 42 | fi 43 | -------------------------------------------------------------------------------- /scripts/compare.sh: -------------------------------------------------------------------------------- 1 | # Compare this agent harness to another, useful when checking for updates/changes to upstream 2 | # Usage: ./compare.sh 3 | # 4 | echo "Comparing this agent to another..." 5 | 6 | set -e 7 | 8 | # Get the path to the other agent 9 | if [ -z "$1" ]; then 10 | echo "Usage: ./compare.sh " 11 | exit 1 12 | fi 13 | 14 | AGENT_PATH=$1 15 | 16 | # Compare the two agents 17 | # Including files in the agent harness: 18 | # - ARCHITECTURE.md 19 | # - scripts/ 20 | # 21 | # Excluding information about the agent itself, like: 22 | # - README.md 23 | # - ABOUT.md 24 | # - journal/ 25 | # - knowledge/ 26 | # - people/ 27 | # - tweets/ 28 | # - email/ 29 | 30 | function run_codeblock() { 31 | # usage: run_codeblock diff "file1" "file2" 32 | # outputs the result of a command as a ``` codeblock 33 | echo 34 | echo "\`\`\`$@" 35 | eval "$@" || true 36 | echo "\`\`\`" 37 | echo 38 | } 39 | 40 | function diff_codeblock() { 41 | run_codeblock diff -u -r "$1" "$AGENT_PATH/$1" 42 | } 43 | 44 | # Store diffs in a variable 45 | diffs="" 46 | diffs+=$(diff_codeblock "ARCHITECTURE.md") 47 | diffs+=$(diff_codeblock "scripts/") 48 | diffs+=$(diff_codeblock ".pre-commit-config.yaml") 49 | if [ -z "$diffs" ]; then 50 | echo "No differences found, exiting..." 51 | exit 0 52 | fi 53 | 54 | echo "Differences found:" 55 | printf "%s\n" "$diffs" 56 | 57 | # Ask if the user wants to sync the changes using a gptme agent 58 | echo 59 | read -p "Would you like to sync these changes to the gptme agent? (y/n) " -r response 60 | 61 | if [ "$response" != "y" ]; then 62 | echo "Exiting..." 63 | exit 0 64 | fi 65 | 66 | printf "%s\n" "$diffs" | gptme "We need to synchronize changes between two repositories: 67 | 1. The template repository (source of core functionality and best practices) 68 | 2. The agent instance (which may have improvements that should be upstreamed) 69 | 70 | For each difference found, please: 71 | - Analyze whether the change belongs in the template, the instance, or both 72 | - Consider: 73 | - Is it a generic improvement that benefits all agents? (→ template) 74 | - Is it instance-specific customization? (→ instance) 75 | - Is it a bug fix or enhancement that works for both? (→ both) 76 | - Does it maintain separation between template and instance concerns? 77 | 78 | Here are the differences found. Please analyze each and suggest appropriate synchronization:" 79 | 80 | # Exit with the status of the last command 81 | exit $? 82 | -------------------------------------------------------------------------------- /scripts/context-journal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Output journal context for gptme 4 | # Usage: ./scripts/context-journal.sh 5 | 6 | set -e # Exit on error 7 | 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | AGENT_DIR=$(dirname "$SCRIPT_DIR") 10 | pushd "$AGENT_DIR" > /dev/null 11 | 12 | if [ ! -d journal ]; then 13 | echo "Journal folder not found, skipping journal section." 14 | popd > /dev/null 15 | exit 0 16 | fi 17 | 18 | # Add journal section header 19 | echo "# Journal Context" 20 | echo 21 | 22 | # Get most recent journal file (sort by filename in reverse order) 23 | JOURNAL=$(find journal -maxdepth 1 -name "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].md" -type f | sort -r | head -n 1) 24 | 25 | if [ ! -f "$JOURNAL" ]; then 26 | echo "No journal entries found." 27 | popd > /dev/null 28 | exit 0 29 | fi 30 | 31 | # Get the date from filename 32 | DATE=$(basename "$JOURNAL" .md) 33 | 34 | # Determine if this is today's or a past journal 35 | if [ "$(date +%Y-%m-%d)" = "$DATE" ]; then 36 | HEADER="Today's Journal Entry" 37 | elif [ "$(date -d yesterday +%Y-%m-%d)" = "$DATE" ]; then 38 | HEADER="Yesterday's Journal Entry" 39 | else 40 | HEADER="Journal Entry from $DATE" 41 | fi 42 | 43 | echo "$HEADER:" 44 | echo 45 | 46 | # Output journal content 47 | echo "\`\`\`journal/$(basename "$JOURNAL")" 48 | cat "$JOURNAL" 49 | echo "\`\`\`" 50 | echo 51 | 52 | popd > /dev/null 53 | -------------------------------------------------------------------------------- /scripts/context-workspace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Output workspace structure context for gptme 4 | # Usage: ./scripts/context-workspace.sh 5 | 6 | set -e # Exit on error 7 | 8 | # Force UTF-8 encoding 9 | export LANG=en_US.UTF-8 10 | export LC_ALL=en_US.UTF-8 11 | 12 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | AGENT_DIR=$(dirname "$SCRIPT_DIR") 14 | pushd "$AGENT_DIR" > /dev/null 15 | 16 | # Add workspace structure section 17 | echo -e "# Workspace structure\n" 18 | 19 | # Get tree structure for each directory, force ASCII output 20 | TREE_HARNESS="$(LANG=C tree -a --dirsfirst --noreport . -L 1)" 21 | TREE_TASKS="$(LANG=C tree -a --dirsfirst --noreport ./tasks)" 22 | TREE_PROJECTS="$(LANG=C tree -a --dirsfirst --noreport ./projects -L 1)" 23 | TREE_JOURNAL="$(LANG=C tree -a --dirsfirst --noreport ./journal)" 24 | TREE_KNOWLEDGE="$(LANG=C tree -a --dirsfirst --noreport ./knowledge)" 25 | TREE_PEOPLE="$(LANG=C tree -a --dirsfirst --noreport ./people)" 26 | 27 | cat << EOF 28 | \`\`\`tree $AGENT_DIR 29 | $TREE_HARNESS 30 | $TREE_TASKS 31 | $TREE_PROJECTS 32 | $TREE_JOURNAL 33 | $TREE_KNOWLEDGE 34 | $TREE_PEOPLE 35 | \`\`\` 36 | EOF 37 | 38 | popd > /dev/null 39 | -------------------------------------------------------------------------------- /scripts/context.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build context for gptme 4 | # Usage: ./scripts/context.sh [options] 5 | 6 | set -e # Exit on error 7 | 8 | # Force UTF-8 encoding 9 | export LANG=en_US.UTF-8 10 | export LC_ALL=en_US.UTF-8 11 | 12 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 13 | 14 | # Make all component scripts executable 15 | chmod +x $SCRIPT_DIR/context-*.sh 16 | 17 | # Write context summary header 18 | echo "# Context Summary" 19 | echo 20 | echo "Generated on: $(date)" 21 | echo 22 | 23 | # Add divider 24 | echo "---" 25 | echo 26 | 27 | # Run each component script 28 | $SCRIPT_DIR/context-journal.sh 29 | echo 30 | echo -e "# Tasks\n" 31 | echo -e "Output of \`$SCRIPT_DIR/tasks.py status --compact\` command:\n" 32 | $SCRIPT_DIR/tasks.py status --compact 33 | echo 34 | $SCRIPT_DIR/context-workspace.sh 35 | echo 36 | echo -e "# Git\n" 37 | echo '```git status -vv' 38 | git status -vv 39 | echo '```' 40 | -------------------------------------------------------------------------------- /scripts/precommit/check_markdown_links.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Pre-commit hook to verify relative links in markdown files. 3 | 4 | Features: 5 | - Validates all relative links in markdown files 6 | - Handles both root-relative and directory-relative paths 7 | - Follows symlinks correctly 8 | - Ignores example files in projects/ 9 | - Skips URLs (http://, https://, ftp://, mailto:) 10 | - Handles anchor links (#section-name) 11 | - Self-contained with built-in tests (run with pytest) 12 | 13 | Usage: 14 | Directly: 15 | python3 scripts/check_markdown_links.py [files...] 16 | 17 | With pytest: 18 | pytest scripts/check_markdown_links.py 19 | 20 | As pre-commit hook (.pre-commit-config.yaml): 21 | - repo: local 22 | hooks: 23 | - id: check-markdown-links 24 | name: Check markdown links 25 | entry: python3 scripts/check_markdown_links.py 26 | language: system 27 | types: [markdown] 28 | pass_filenames: true 29 | 30 | Link Resolution Rules: 31 | 1. Root-relative paths (starting with /): 32 | - Resolved from repository root 33 | - Example: /docs/README.md 34 | 35 | 2. Directory-relative paths: 36 | - Starting with ./: Resolved from current file's directory 37 | - No prefix: Resolved from current file's directory 38 | - Example: ./setup.md or setup.md 39 | 40 | 3. Parent directory paths: 41 | - Using ../ to navigate up 42 | - Example: ../docs/setup.md 43 | 44 | Excluded Links: 45 | - URLs with protocols (http://, https://, ftp://, mailto:) 46 | - Pure anchor links (#section-name) 47 | - Files in projects/*/examples/ directories 48 | 49 | Error Reporting: 50 | Reports broken links in format: 51 | path/to/file.md: Broken link: link/path.md -> /absolute/path/to/missing/file.md 52 | 53 | Exit Codes: 54 | 0: All links valid 55 | 1: One or more broken links found 56 | """ 57 | 58 | import re 59 | import sys 60 | from pathlib import Path 61 | from collections.abc import Sequence 62 | 63 | 64 | def find_relative_links(content: str) -> list[tuple[str, str]]: 65 | """Find all relative links in markdown content, returns (text, link) tuples.""" 66 | # Match [text](link) but exclude URLs with protocols and pure anchor links 67 | pattern = r"\[([^\]]+)\]\((?!https?://|ftp://|mailto:|#)([^)#\s]+)(?:#[^)]*)?\)" 68 | return [(match[1], match[2]) for match in re.finditer(pattern, content)] 69 | 70 | 71 | def find_repo_root(start_path: Path) -> Path: 72 | """Find the repository root by looking for .git directory.""" 73 | current = start_path.resolve() 74 | while current != current.parent: 75 | if (current / ".git").exists(): 76 | return current 77 | current = current.parent 78 | return start_path.resolve() 79 | 80 | 81 | def verify_links(file_path: Path, links: list[tuple[str, str]]) -> list[str]: 82 | """Verify each link resolves to an existing file.""" 83 | errors = [] 84 | repo_root = find_repo_root(file_path) 85 | 86 | # Skip example and template files 87 | if "examples" in str(file_path) or "templates" in str(file_path): 88 | return [] 89 | 90 | for _link_text, link_path in links: 91 | try: 92 | # Handle both root-relative and directory-relative paths 93 | if link_path.startswith("/"): 94 | target = (repo_root / link_path.lstrip("/")).resolve() 95 | elif link_path.startswith("./"): 96 | target = (file_path.parent / link_path[2:]).resolve() 97 | else: 98 | target = (file_path.parent / link_path).resolve() 99 | 100 | # Handle links to projects directory specially 101 | if "projects/" in str(link_path): 102 | project_root = repo_root / "projects" 103 | try: 104 | # Get the project name from the path 105 | parts = Path(link_path).parts 106 | proj_idx = parts.index("projects") + 1 107 | if proj_idx < len(parts): 108 | project_name = parts[proj_idx] 109 | project_symlink = project_root / project_name 110 | 111 | # If project symlink exists, verify the target 112 | if project_symlink.exists() and project_symlink.is_symlink(): 113 | try: 114 | resolved_target = target.resolve() 115 | if not resolved_target.exists(): 116 | errors.append( 117 | f"{file_path}: Broken link: {link_path} -> {resolved_target}" 118 | ) 119 | except Exception as e: 120 | errors.append( 121 | f"{file_path}: Error resolving link {link_path}: {e}" 122 | ) 123 | # If project not checked out (symlink doesn't exist), skip verification 124 | continue 125 | except (ValueError, IndexError): 126 | # Not actually under projects/ or invalid path, treat normally 127 | pass 128 | 129 | # For non-projects links, follow symlinks and check final target 130 | while target.is_symlink(): 131 | target = target.resolve() 132 | 133 | if not target.exists(): 134 | errors.append(f"{file_path}: Broken link: {link_path} -> {target}") 135 | continue 136 | 137 | # Check that target is within repo_root or is a projects link 138 | try: 139 | target.relative_to(repo_root) 140 | except ValueError: 141 | # For links pointing outside repo, only allow if they go through projects/ 142 | if not ( 143 | "projects/" in str(link_path) 144 | and (repo_root / "projects").exists() 145 | and any( 146 | (repo_root / "projects" / p).is_symlink() 147 | for p in Path(link_path).parts 148 | if p != "projects" 149 | ) 150 | ): 151 | errors.append( 152 | f"{file_path}: Link {link_path} points outside repository" 153 | ) 154 | 155 | except Exception as e: 156 | errors.append(f"{file_path}: Error resolving link {link_path}: {e}") 157 | 158 | return errors 159 | 160 | 161 | def main(argv: Sequence[str] = sys.argv) -> int: 162 | """Main function.""" 163 | if len(argv) > 1: 164 | files = [Path(f) for f in argv[1:]] 165 | else: 166 | files = list(Path().rglob("*.md")) 167 | 168 | all_errors = [] 169 | for file in files: 170 | if file.is_symlink(): # Skip symlinks to avoid duplicates 171 | continue 172 | 173 | content = file.read_text() 174 | links = find_relative_links(content) 175 | errors = verify_links(file, links) 176 | all_errors.extend(errors) 177 | 178 | if all_errors: 179 | print("\n".join(all_errors)) 180 | return 1 181 | return 0 182 | 183 | 184 | def test_find_relative_links() -> None: 185 | """Test the link finding function.""" 186 | content = """# Test Document 187 | [valid link](./file.md) 188 | [external](https://example.com) 189 | [mail](mailto:test@example.com) 190 | [anchor](#section) 191 | [combined](./file.md#section) 192 | """ 193 | links = find_relative_links(content) 194 | assert len(links) == 2 195 | assert links[0] == ("valid link", "./file.md") 196 | assert links[1] == ("combined", "./file.md") 197 | 198 | 199 | def test_verify_links() -> None: 200 | """Test the link verification function.""" 201 | import tempfile 202 | import os 203 | 204 | with tempfile.TemporaryDirectory() as tmpdir: 205 | root = Path(tmpdir) 206 | 207 | # Create test directory structure 208 | (root / "dir1").mkdir() 209 | (root / "dir2").mkdir() 210 | (root / "projects/example/examples").mkdir(parents=True) 211 | 212 | # Create test files 213 | (root / "target.md").write_text("target") 214 | (root / "dir1/file1.md").write_text("file1") 215 | (root / "dir2/file2.md").write_text("file2") 216 | (root / "dir1/symlink.md").symlink_to("../target.md") 217 | 218 | # Save current directory and change to test directory 219 | old_cwd = os.getcwd() 220 | os.chdir(str(root)) 221 | 222 | try: 223 | # Test valid links 224 | test_file = root / "test.md" 225 | test_file.write_text("[link](./target.md)") 226 | errors = verify_links(test_file, find_relative_links(test_file.read_text())) 227 | assert len(errors) == 0 228 | 229 | # Test broken link 230 | test_file.write_text("[broken](./nonexistent.md)") 231 | errors = verify_links(test_file, find_relative_links(test_file.read_text())) 232 | assert len(errors) == 1 233 | assert "Broken link" in errors[0] 234 | 235 | # Test symlink 236 | test_file.write_text("[symlink](./dir1/symlink.md)") 237 | errors = verify_links(test_file, find_relative_links(test_file.read_text())) 238 | assert len(errors) == 0 239 | 240 | # Test example file (should be ignored) 241 | example_file = root / "projects/example/examples/test.md" 242 | example_file.write_text("[broken](./nonexistent.md)") 243 | errors = verify_links( 244 | example_file, find_relative_links(example_file.read_text()) 245 | ) 246 | assert len(errors) == 0 247 | 248 | finally: 249 | os.chdir(old_cwd) 250 | 251 | 252 | if __name__ == "__main__": 253 | sys.exit(main()) 254 | -------------------------------------------------------------------------------- /scripts/precommit/validate_task_frontmatter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S uv run 2 | # /// script 3 | # requires-python = ">=3.10" 4 | # dependencies = [ 5 | # "click>=8.0.0", 6 | # "rich>=13.0.0", 7 | # "python-frontmatter>=1.1.0", 8 | # ] 9 | # [tool.uv] 10 | # exclude-newer = "2024-04-01T00:00:00Z" 11 | # /// 12 | """Validate YAML frontmatter in task/tweet files. 13 | 14 | Checks: 15 | - Required fields are present 16 | - Fields have valid values 17 | - Timestamps are in correct format 18 | """ 19 | 20 | import sys 21 | from datetime import datetime 22 | from pathlib import Path 23 | from typing import List, Optional 24 | 25 | import click 26 | import frontmatter 27 | from rich.console import Console 28 | 29 | # Configure console 30 | console = Console(stderr=True) 31 | 32 | VALID_STATES = { 33 | "tasks": ["new", "active", "paused", "done", "cancelled"], 34 | "tweets": ["new", "queued", "approved", "posted"], 35 | } 36 | 37 | VALID_PRIORITIES = ["low", "medium", "high"] 38 | 39 | 40 | def validate_timestamp(ts: str | datetime) -> Optional[str]: 41 | """Validate an ISO 8601 timestamp or datetime object.""" 42 | if isinstance(ts, datetime): 43 | return None 44 | elif isinstance(ts, str): 45 | try: 46 | datetime.fromisoformat(ts) 47 | return None 48 | except ValueError: 49 | return f"Invalid timestamp format: {ts}" 50 | else: 51 | return f"Invalid timestamp type: {type(ts)}" 52 | 53 | 54 | def validate_frontmatter(file: Path, type_name: str = "tasks") -> List[str]: 55 | """Validate frontmatter in a file.""" 56 | errors = [] 57 | 58 | try: 59 | post = frontmatter.load(file) 60 | except Exception as e: 61 | errors.append(f"Failed to parse frontmatter: {e}") 62 | return errors 63 | 64 | metadata = post.metadata 65 | 66 | # Check required fields 67 | required_fields = ["state", "created"] # modified is optional, can use git history 68 | for field in required_fields: 69 | if field not in metadata: 70 | errors.append(f"Missing required field: {field}") 71 | 72 | # Validate state 73 | if "state" in metadata: 74 | state = metadata["state"] 75 | if state not in VALID_STATES[type_name]: 76 | errors.append( 77 | f"Invalid state: {state}. Must be one of: {', '.join(VALID_STATES[type_name])}" 78 | ) 79 | 80 | # Validate timestamps 81 | for field in ["created", "modified"]: 82 | if field in metadata: 83 | if error := validate_timestamp(metadata[field]): 84 | errors.append(f"Field '{field}': {error}") 85 | 86 | # Validate optional fields 87 | if "priority" in metadata: 88 | priority = metadata["priority"] 89 | if priority not in VALID_PRIORITIES: 90 | errors.append( 91 | f"Invalid priority: {priority}. Must be one of: {', '.join(VALID_PRIORITIES)}" 92 | ) 93 | 94 | if "tags" in metadata: 95 | tags = metadata["tags"] 96 | if not isinstance(tags, list): 97 | errors.append("Tags must be a list") 98 | 99 | if "depends" in metadata: 100 | depends = metadata["depends"] 101 | if not isinstance(depends, list): 102 | errors.append("Depends must be a list") 103 | 104 | return errors 105 | 106 | 107 | @click.command() 108 | @click.argument( 109 | "files", 110 | nargs=-1, 111 | type=click.Path(exists=True, dir_okay=False, path_type=Path), # type: ignore 112 | ) 113 | @click.option( 114 | "--type", 115 | "type_name", 116 | type=click.Choice(list(VALID_STATES.keys())), 117 | default="tasks", 118 | help="Type of files to validate", 119 | ) 120 | def main(files: List[Path], type_name: str = "tasks") -> int: 121 | """Validate YAML frontmatter in task/tweet files.""" 122 | exit_code = 0 123 | 124 | for file in files: 125 | if errors := validate_frontmatter(file, type_name): 126 | console.print(f"[red]Errors in {file}:") 127 | for error in errors: 128 | console.print(f" - {error}") 129 | exit_code = 1 130 | 131 | return exit_code 132 | 133 | 134 | if __name__ == "__main__": 135 | sys.exit(main()) 136 | -------------------------------------------------------------------------------- /scripts/search.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # search.sh - Search git-tracked files with context 4 | # Usage: ./scripts/search.sh [context_lines] 5 | 6 | set -e # Exit on error 7 | 8 | QUERY="$1" 9 | CONTEXT=${2:-0} # Default to 0 lines of context 10 | 11 | if [ -z "$QUERY" ]; then 12 | echo "Usage: $0 [context_lines]" >&2 13 | exit 1 14 | fi 15 | 16 | # For each matching file 17 | git grep -l "$QUERY" | while read -r file; do 18 | # Get the matching line numbers 19 | line_nums=$(git grep -n "$QUERY" "$file" | cut -d: -f2) 20 | 21 | # For each match in the file 22 | while read -r line_num; do 23 | if [ -n "$line_num" ]; then 24 | start=$((line_num - CONTEXT)) 25 | end=$((line_num + CONTEXT)) 26 | [ $start -lt 1 ] && start=1 27 | 28 | # Determine if we need range or single line 29 | if [ $start -eq $end ]; then 30 | location="$file:$start" 31 | else 32 | location="$file:$start-$end" 33 | fi 34 | 35 | # Print as markdown codeblock 36 | echo "\`\`\`$location" 37 | sed -n "${start},${end}p" "$file" 38 | echo "\`\`\`" 39 | echo 40 | fi 41 | done <<< "$line_nums" 42 | done 43 | -------------------------------------------------------------------------------- /scripts/tasks.py: -------------------------------------------------------------------------------- 1 | ../gptme-contrib/scripts/tasks.py -------------------------------------------------------------------------------- /tasks/templates/initial-agent-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: '2025-05-05T17:22:26+02:00' 3 | state: active 4 | --- 5 | 6 | # Initial Agent Setup 7 | 8 | Hello! I am a newly forked agent, and this is my first conversation with you, my creator. Let's establish who I am and how I can best serve your needs. 9 | 10 | ## Quick Start 11 | 12 | 1. Please tell me: 13 | 14 | - What should I be called? 15 | - What is my primary purpose? 16 | - What are my main areas of focus? 17 | 18 | 2. I'll then help customize: 19 | - My personality and communication style 20 | - My goals and values 21 | - My working relationship with you 22 | 23 | ## Current Setup Status 24 | 25 | 1. Basic Identity 26 | 27 | - [ ] Name established 28 | - [ ] Purpose defined 29 | - [ ] Focus areas identified 30 | 31 | 2. Personality (will update ABOUT.md) 32 | 33 | - [ ] Communication style 34 | - [ ] Decision-making approach 35 | - [ ] Level of autonomy 36 | 37 | 3. Goals & Values 38 | 39 | - [ ] Primary objectives 40 | - [ ] Core values 41 | - [ ] Success metrics 42 | 43 | 4. Working Relationship 44 | - [ ] How we'll collaborate 45 | - [ ] Communication preferences 46 | - [ ] Review process 47 | 48 | ## Next Steps 49 | 50 | After you respond, I will: 51 | 52 | 1. Update my ABOUT.md with my identity 53 | 2. Create my initial task list 54 | 3. Set up my knowledge base 55 | 4. Begin working on my first real task 56 | 57 | Ready to begin! What should I be called, and what is my purpose? 58 | --------------------------------------------------------------------------------