Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
name: Backup Dismissed Tasks
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */6 * * *' # Every 6 hours
|
||||
workflow_dispatch: {}
|
||||
|
||||
concurrency:
|
||||
group: repo-file-writes
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
backup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch dismissed task IDs from OrgMyLife
|
||||
run: |
|
||||
# Get all dismissed and completed tasks (their source_ids)
|
||||
RESPONSE=$(curl -s -u "${{ secrets.ORGMYLIFE_USER }}:${{ secrets.ORGMYLIFE_PASS }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.ORGMYLIFE_API_SECRET }}" \
|
||||
"https://api.andreknie.de/api/tasks/dismissed")
|
||||
echo "$RESPONSE" > dismissed_tasks.json
|
||||
cat dismissed_tasks.json | head -c 200
|
||||
|
||||
- name: Commit if changed
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add dismissed_tasks.json
|
||||
git diff --cached --quiet && echo "No changes" && exit 0
|
||||
git commit -m "chore: backup dismissed task IDs [skip ci]"
|
||||
git pull --rebase origin main || true
|
||||
git push
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Mark Issues for Review
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'DONE_ISSUES.md'
|
||||
|
||||
concurrency:
|
||||
group: repo-file-writes
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
mark-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Mark issues as ready for review
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const content = fs.readFileSync('DONE_ISSUES.md', 'utf8');
|
||||
const issueNumbers = [...content.matchAll(/#(\d+)/g)].map(m => parseInt(m[1]));
|
||||
|
||||
if (issueNumbers.length === 0) {
|
||||
console.log('No issue numbers found in DONE_ISSUES.md');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const num of issueNumbers) {
|
||||
try {
|
||||
const issue = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: num,
|
||||
});
|
||||
|
||||
if (issue.data.state === 'open') {
|
||||
// Add review label
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: num,
|
||||
labels: ['ready-for-review'],
|
||||
});
|
||||
|
||||
// Add a comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: num,
|
||||
body: '✅ Implementation complete and deployed. Ready for your review.\n\nClose this issue when you\'re satisfied with the result.',
|
||||
});
|
||||
|
||||
console.log(`Marked #${num} as ready-for-review`);
|
||||
} else {
|
||||
console.log(`Issue #${num} already closed.`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Error processing #${num}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the file after processing
|
||||
fs.writeFileSync('DONE_ISSUES.md', '# Done Issues\n\n_Empty — no issues to process._\n');
|
||||
|
||||
- name: Commit cleared file
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add DONE_ISSUES.md
|
||||
git diff --cached --quiet && exit 0
|
||||
git commit -m "chore: clear DONE_ISSUES.md after processing [skip ci]"
|
||||
git pull --rebase origin main || true
|
||||
git push
|
||||
@@ -0,0 +1,80 @@
|
||||
name: Collect AI Tasks
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/30 * * * *'
|
||||
workflow_dispatch: {}
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
sync-tasks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch agent-ready tasks from OrgMyLife
|
||||
id: fetch
|
||||
run: |
|
||||
curl -s -u "${{ secrets.ORGMYLIFE_USER }}:${{ secrets.ORGMYLIFE_PASS }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.ORGMYLIFE_API_SECRET }}" \
|
||||
"https://api.andreknie.de/api/orchestrator/tasks" > /tmp/tasks.json
|
||||
cat /tmp/tasks.json | head -c 500
|
||||
|
||||
- name: Create GitHub Issues for new tasks
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const data = JSON.parse(fs.readFileSync('/tmp/tasks.json', 'utf8'));
|
||||
const tasks = data.tasks || [];
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.log('No agent-ready tasks found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const existingIssues = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
labels: 'ai-task',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const existingTitles = new Set(
|
||||
existingIssues.data.map(i => i.title)
|
||||
);
|
||||
|
||||
for (const task of tasks) {
|
||||
const issueTitle = `[AI] ${task.identifier}: ${task.title}`;
|
||||
|
||||
if (existingTitles.has(issueTitle)) {
|
||||
console.log(`Skipping (already exists): ${issueTitle}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const body = [
|
||||
`**Task ID:** ${task.identifier}`,
|
||||
`**Priority:** ${task.priority || 'unset'}`,
|
||||
`**State:** ${task.state}`,
|
||||
task.due_date ? `**Due:** ${task.due_date}` : '',
|
||||
'',
|
||||
'## Description',
|
||||
task.description || '_No description provided._',
|
||||
'',
|
||||
'---',
|
||||
'_Auto-created from OrgMyLife task board._',
|
||||
].filter(Boolean).join('\n');
|
||||
|
||||
await github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: issueTitle,
|
||||
body: body,
|
||||
labels: ['ai-task'],
|
||||
});
|
||||
|
||||
console.log(`Created issue: ${issueTitle}`);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
name: Update Task State on Issue Events
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed, labeled]
|
||||
|
||||
jobs:
|
||||
mark-review:
|
||||
if: |
|
||||
github.event.action == 'labeled' &&
|
||||
github.event.label.name == 'ready-for-review' &&
|
||||
contains(github.event.issue.labels.*.name, 'ai-task')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark task as review in OrgMyLife
|
||||
run: |
|
||||
TITLE="${{ github.event.issue.title }}"
|
||||
TASK_ID=$(echo "$TITLE" | grep -oP 'OML-\K[0-9]+')
|
||||
|
||||
if [ -z "$TASK_ID" ]; then
|
||||
echo "Could not extract task ID from: $TITLE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Marking task $TASK_ID as review..."
|
||||
|
||||
curl -s -X POST \
|
||||
-u "${{ secrets.ORGMYLIFE_USER }}:${{ secrets.ORGMYLIFE_PASS }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.ORGMYLIFE_API_SECRET }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "review"}' \
|
||||
"https://api.andreknie.de/api/orchestrator/tasks/${TASK_ID}/state"
|
||||
|
||||
mark-done:
|
||||
if: |
|
||||
github.event.action == 'closed' &&
|
||||
contains(github.event.issue.labels.*.name, 'ai-task')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark task as completed in OrgMyLife
|
||||
run: |
|
||||
TITLE="${{ github.event.issue.title }}"
|
||||
TASK_ID=$(echo "$TITLE" | grep -oP 'OML-\K[0-9]+')
|
||||
|
||||
if [ -z "$TASK_ID" ]; then
|
||||
echo "Could not extract task ID from: $TITLE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Marking task $TASK_ID as completed..."
|
||||
|
||||
curl -s -X POST \
|
||||
-u "${{ secrets.ORGMYLIFE_USER }}:${{ secrets.ORGMYLIFE_PASS }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.ORGMYLIFE_API_SECRET }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "completed"}' \
|
||||
"https://api.andreknie.de/api/orchestrator/tasks/${TASK_ID}/state"
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
name: Deploy to VPS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Write .env file to VPS
|
||||
uses: appleboy/ssh-action@v1
|
||||
env:
|
||||
ENV_CONTENT: |
|
||||
POSTGRES_PASSWORD=${{ secrets.ENV_POSTGRES_PASSWORD }}
|
||||
API_SECRET=${{ secrets.ENV_API_SECRET }}
|
||||
APP_USERNAME=${{ secrets.ENV_APP_USERNAME }}
|
||||
APP_PASSWORD=${{ secrets.ENV_APP_PASSWORD }}
|
||||
NEXTCLOUD_URL=${{ secrets.ENV_NEXTCLOUD_URL }}
|
||||
NEXTCLOUD_USERNAME=${{ secrets.ENV_NEXTCLOUD_USERNAME }}
|
||||
NEXTCLOUD_PASSWORD=${{ secrets.ENV_NEXTCLOUD_PASSWORD }}
|
||||
IMAP_SERVER=${{ secrets.ENV_IMAP_SERVER }}
|
||||
IMAP_PORT=993
|
||||
EMAIL_USERNAME=${{ secrets.ENV_EMAIL_USERNAME }}
|
||||
EMAIL_PASSWORD=${{ secrets.ENV_EMAIL_PASSWORD }}
|
||||
EMAIL_WEBMAIL_URL=${{ secrets.ENV_EMAIL_WEBMAIL_URL }}
|
||||
GMAIL_USERNAME=${{ secrets.ENV_GMAIL_USERNAME }}
|
||||
GMAIL_PASSWORD=${{ secrets.ENV_GMAIL_PASSWORD }}
|
||||
with:
|
||||
host: 217.160.174.2
|
||||
username: root
|
||||
key: ${{ secrets.VPS_SSH_KEY }}
|
||||
envs: ENV_CONTENT
|
||||
script: |
|
||||
# Ensure directory exists
|
||||
if [ ! -d /opt/OrgMyLife ]; then
|
||||
cd /opt
|
||||
git clone https://github.com/DoctoDre/OrgMyLife.git || true
|
||||
fi
|
||||
|
||||
cd /opt/OrgMyLife
|
||||
git pull origin main || true
|
||||
|
||||
# Write .env from environment variable (safe for special chars)
|
||||
printf '%s\n' "$ENV_CONTENT" > .env
|
||||
|
||||
docker compose up -d --build
|
||||
echo "Deploy complete"
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Sync Issues to File
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/30 * * * *'
|
||||
workflow_dispatch: {}
|
||||
issues:
|
||||
types: [opened, closed, reopened, edited, labeled, unlabeled]
|
||||
|
||||
concurrency:
|
||||
group: repo-file-writes
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Write open AI issues to file
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const issues = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
labels: 'ai-task',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
let content = '# AI Tasks (auto-generated)\n\n';
|
||||
content += `_Last updated: ${new Date().toISOString()}_\n\n`;
|
||||
|
||||
if (issues.data.length === 0) {
|
||||
content += 'No open AI tasks.\n';
|
||||
} else {
|
||||
content += `${issues.data.length} open task(s):\n\n`;
|
||||
for (const issue of issues.data) {
|
||||
content += `---\n\n`;
|
||||
content += `## #${issue.number}: ${issue.title}\n\n`;
|
||||
content += `- **State:** open\n`;
|
||||
content += `- **Created:** ${issue.created_at}\n`;
|
||||
content += `- **URL:** ${issue.html_url}\n\n`;
|
||||
content += `### Description\n\n`;
|
||||
content += `${issue.body || '_No description._'}\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('AI_TASKS.md', content);
|
||||
console.log(`Wrote ${issues.data.length} issues to AI_TASKS.md`);
|
||||
|
||||
- name: Commit and push if changed
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add AI_TASKS.md
|
||||
git diff --cached --quiet && echo "No changes" && exit 0
|
||||
git commit -m "chore: update AI_TASKS.md [skip ci]"
|
||||
git pull --rebase origin main || true
|
||||
git push
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Sync OrgMyLife Plan to Projects Board
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: ['PLAN.md']
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Parse open tasks and sync to Projects board
|
||||
run: |
|
||||
REPO_NAME="OrgMyLife"
|
||||
|
||||
TASKS="[]"
|
||||
if [ -f "PLAN.md" ]; then
|
||||
ITEMS=$(grep -E '^\s*- \[ \]' PLAN.md | sed 's/.*- \[ \] //' | head -50)
|
||||
if [ -n "$ITEMS" ]; then
|
||||
TASKS=$(echo "$ITEMS" | jq -R -s 'split("\n") | map(select(length > 0)) | map({"title": ., "status": "backlog"})')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Found tasks: $TASKS"
|
||||
|
||||
PAYLOAD=$(jq -n --arg repo "$REPO_NAME" --argjson tasks "$TASKS" '{"repo": $repo, "tasks": $tasks}')
|
||||
|
||||
curl -s -X POST \
|
||||
-u "${{ secrets.ORGMYLIFE_USER }}:${{ secrets.ORGMYLIFE_PASS }}" \
|
||||
-H "X-API-Key: ${{ secrets.ORGMYLIFE_API_SECRET }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"https://api.andreknie.de/api/projects/sync"
|
||||
Reference in New Issue
Block a user